PluginProbe ʕ •ᴥ•ʔ
WooCommerce / 10.9.4
WooCommerce v10.9.4
10.9.4 10.9.3 10.9.2 10.9.1 10.9.0 10.9.0-rc.1 10.9.0-beta.2 10.9.0-beta.1 10.8.1 10.8.0 10.8.0-rc.1 10.8.0-beta.2 10.8.0-beta.1 7.8.0-beta.1 7.8.0-beta.2 7.8.0-rc.1 7.8.0-rc.2 7.8.1 7.8.2 7.8.3 7.8.4 7.9.0 7.9.0-beta.1 7.9.0-beta.2 7.9.0-rc.2 7.9.0-rc.3 7.9.1 7.9.2 8.0.0 8.0.0-beta.1 8.0.0-beta.2 8.0.0-rc.1 8.0.0-rc.2 8.0.1 8.0.2 8.0.3 8.0.4 8.0.5 8.1.0 8.1.0-beta.1 8.1.0-rc.1 8.1.0-rc.2 8.1.1 8.1.2 8.1.3 8.1.4 8.2.0 8.2.0-beta.1 8.2.0-rc.1 8.2.0-rc.2 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.3.0 8.3.0-beta.1 8.3.0-rc.1 8.3.0-rc.2 8.3.1 8.3.2 8.3.3 8.3.4 8.4.0 8.4.0-beta.1 8.4.0-rc.1 8.4.1 8.4.2 8.4.3 8.5.0 8.5.0-beta.1 8.5.0-rc.1 8.5.1 8.5.2 8.5.3 8.5.4 8.5.5 8.6.0 8.6.0-beta.1 8.6.0-rc.1 8.6.1 8.6.2 8.6.3 8.6.4 8.7.0 8.7.0-beta.1 8.7.0-beta.2 8.7.0-rc.1 8.7.1 8.7.2 8.7.3 8.8.0 8.8.0-beta.1 8.8.0-rc.1 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.8.6 8.8.7 8.9.0 8.9.0-beta.1 8.9.0-rc.1 8.9.1 8.9.2 8.9.3 8.9.4 8.9.5 9.0.0 9.0.0-beta.1 9.0.0-beta.2 9.0.0-rc.1 9.0.1 9.0.2 9.0.3 9.0.4 9.1.0 9.1.0-beta.1 9.1.0-rc.1 9.1.1 9.1.2 9.1.3 9.1.4 9.1.5 9.1.6 9.2.0 9.2.0-beta.1 9.2.0-rc.1 9.2.1 9.2.2 9.2.3 9.2.4 9.2.5 9.3.0 9.3.0-beta.1 9.3.0-rc.1 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.3.6 9.4.0 9.4.0-beta.1 9.4.0-beta.2 9.4.0-rc.1 9.4.0-rc.2 9.4.0-rc.3 9.4.0-rc.4 9.4.1 9.4.2 9.4.3 9.4.4 9.4.5 9.5.0 9.5.0-beta.1 9.5.0-beta.2 9.5.0-rc.1 9.5.1 9.5.2 9.5.3 9.5.4 9.6.0 9.6.0-beta.1 9.6.0-beta.2 9.6.0-rc.1 9.6.1 9.6.2 9.6.3 9.6.4 9.7.0 9.7.0-beta.1 9.7.0-rc.1 9.7.1 9.7.2 9.7.3 9.8.0 9.8.0-beta.1 9.8.0-rc.1 9.8.1 9.8.2 9.8.3 9.8.4 9.8.5 9.8.6 9.8.7 9.9.0 9.9.0-beta.1 9.9.0-rc.1 9.9.1 9.9.2 9.9.3 9.9.4 9.9.5 9.9.6 9.9.7 3.7.3 7.1.2 3.8.0 7.2.0 3.8.0-beta.1 7.2.0-beta.1 3.8.0-rc.1 7.2.0-beta.2 3.8.0-rc.2 7.2.0-rc.1 3.8.1 7.2.0-rc.2 3.8.2 7.2.1 3.8.3 7.2.2 3.9.0 7.2.3 3.9.0-beta.1 7.2.4 3.9.0-beta.2 7.3.0 3.9.0-rc.1 7.3.0-beta.1 3.9.0-rc.2 7.3.0-beta.2 3.9.0-rc.3 7.3.0-rc.1 3.9.0-rc.4 7.3.0-rc.2 3.9.1 7.3.1 3.9.2 7.4.0 3.9.3 7.4.0-beta.1 3.9.4 7.4.0-beta.2 3.9.5 7.4.0-rc.1 4.0.0 7.4.0-rc.2 4.0.0-beta.1 7.4.1 4.0.0-rc.1 7.4.2 4.0.0-rc.2 7.5.0 4.0.1 7.5.0-beta.1 4.0.2 7.5.0-beta.2 4.0.3 7.5.0-rc.1 4.0.4 7.5.1 4.1.0 7.5.2 4.1.0-beta.1 7.6.0 4.1.0-beta.2 7.6.0-beta.1 4.1.0-rc.1 7.6.0-beta.2 4.1.0-rc.2 7.6.0-rc.1 4.1.1 7.6.0-rc.2 4.1.2 7.6.0-rc.3 4.1.3 7.6.1 4.1.4 7.6.2 4.2.0 7.7.0 4.2.0-RC.1 7.7.0-beta.1 4.2.0-RC.2 7.7.0-beta.2 4.2.0-beta.1 7.7.0-rc.1 4.2.1 7.7.1 4.2.2 7.7.2 4.2.3 7.7.3 4.2.4 7.8.0 4.2.5 4.3.0 4.3.0-beta.1 4.3.0-rc.1 4.3.0-rc.2 4.3.0-rc.3 4.3.1 4.3.2 4.3.3 4.3.4 4.3.5 4.3.6 4.4.0 4.4.0-beta.1 4.4.0-rc.1 4.4.1 4.4.2 4.4.3 4.4.4 4.5.0 4.5.0-beta.1 4.5.0-rc.1 4.5.0-rc.3 4.5.1 4.5.2 4.5.3 4.5.4 4.5.5 4.6.0 4.6.0-beta.1 4.6.0-rc.1 4.6.1 4.6.2 4.6.3 4.6.4 4.6.5 4.7.0 4.7.0-beta.1 4.7.0-beta.2 4.7.0-rc.1 4.7.1 4.7.1-beta.1 4.7.2 4.7.3 4.7.4 4.8.0 4.8.0-beta.1 4.8.0-rc.1 4.8.0-rc.2 4.8.1 4.8.2 4.8.3 4.9.0 4.9.0-beta.1 4.9.0-rc.1 4.9.0-rc.2 4.9.1 4.9.2 4.9.3 4.9.4 4.9.5 5.0.0 5.0.0-beta.1 5.0.0-beta.2 5.0.0-rc.1 5.0.0-rc.2 5.0.0-rc.3 5.0.1 5.0.2 5.0.3 5.1.0 5.1.0-beta.1 5.1.0-rc.1 trunk 5.1.1 10.0.0 5.1.2 10.0.0-rc.1 5.1.3 10.0.0-rc.2 5.2.0 10.0.1 5.2.0-beta.1 10.0.2 5.2.0-rc.1 10.0.3 5.2.0-rc.2 10.0.4 5.2.1 10.0.5 5.2.2 10.0.6 5.2.3 10.1.0 5.2.4 10.1.0-rc.1 5.2.5 10.1.0-rc.2 5.3.0 10.1.0-rc.3 5.3.0-beta.1 10.1.0-rc.4 5.3.0-rc.1 10.1.1 5.3.0-rc.2 10.1.2 5.3.1 10.1.3 5.3.2 10.1.4 5.3.3 10.2.0 5.4.0 10.2.0-beta.1 5.4.0-beta.1 10.2.0-beta.2 5.4.0-rc.1 10.2.0-rc.1 5.4.1 10.2.1 5.4.2 10.2.2 5.4.3 10.2.3 5.4.4 10.2.4 5.4.5 10.3.0 5.5.0 10.3.0-beta.1 5.5.0-beta.1 10.3.0-beta.2 5.5.0-rc.1 10.3.0-rc.1 5.5.0-rc.2 10.3.0-rc.2 5.5.1 10.3.1 5.5.2 10.3.2 5.5.3 10.3.3 5.5.4 10.3.4 5.5.5 10.3.5 5.6.0 10.3.6 5.6.0-beta.1 10.3.7 5.6.0-rc.1 10.3.8 5.6.0-rc.2 10.4.0 5.6.1 10.4.0-beta.1 5.6.2 10.4.0-beta.2 5.6.3 10.4.0-rc.1 5.7.0 10.4.1 5.7.0-beta.1 10.4.2 5.7.0-rc.1 10.4.3 5.7.1 10.4.4 5.7.2 10.5.0 5.7.3 10.5.0-beta.1 5.8.0 10.5.0-beta.2 5.8.0-beta.1 10.5.0-rc.1 5.8.0-beta.2 10.5.0-rc.2 5.8.0-rc.1 10.5.0-rc.3 5.8.1 10.5.1 5.8.2 10.5.2 5.9.0 10.5.3 5.9.0-beta.1 10.6.0 5.9.0-rc.1 10.6.0-beta.1 5.9.0-rc.2 10.6.0-beta.2 5.9.1 10.6.0-rc.1 5.9.2 10.6.1 6.0.0 10.6.2 6.0.0-beta.1 10.7.0 6.0.0-rc.1 10.7.0-beta.1 6.0.1 10.7.0-beta.2 6.0.2 10.7.0-rc.1 6.1.0 3.0.0 6.1.0-beta.1 3.0.1 6.1.0-rc.1 3.0.2 6.1.0-rc.2 3.0.3 6.1.1 3.0.4 6.1.2 3.0.5 6.1.3 3.0.6 6.2.0 3.0.7 6.2.0-beta.1 3.0.8 6.2.0-rc.1 3.0.9 6.2.0-rc.2 3.1.0 6.2.1 3.1.1 6.2.2 3.1.2 6.2.3 3.2.0 6.3.0 3.2.1 6.3.0-beta.1 3.2.2 6.3.0-rc.1 3.2.3 6.3.0-rc.2 3.2.4 6.3.1 3.2.5 6.3.2 3.2.6 6.4.0 3.3.0 6.4.0-beta.1 3.3.1 6.4.0-rc.1 3.3.2 6.4.1 3.3.2-rc.1 6.4.2 3.3.3 6.5.0 3.3.4 6.5.0-beta.1 3.3.5 6.5.0-rc.1 3.3.6 6.5.0-rc.2 3.4.0 6.5.1 3.4.0-beta.1 6.5.2 3.4.0-rc.2 6.6.0 3.4.1 6.6.0-beta.1 3.4.2 6.6.0-rc.1 3.4.3 6.6.0-rc.2 3.4.4 6.6.1 3.4.5 6.6.2 3.4.6 6.7.0 3.4.7 6.7.0-beta.1 3.4.8 6.7.0-beta.2 3.5.0 6.7.0-rc.1 3.5.0-beta.1 6.7.1 3.5.0-rc.1 6.8.0 3.5.0-rc.2 6.8.0-beta.1 3.5.1 6.8.0-beta.2 3.5.10 6.8.0-rc.1 3.5.2 6.8.1 3.5.3 6.8.2 3.5.4 6.8.3 3.5.5 6.9.0 3.5.6 6.9.0-beta.1 3.5.7 6.9.0-beta.2 3.5.8 6.9.0-rc.1 3.5.9 6.9.1 3.6.0 6.9.2 3.6.0-beta.1 6.9.3 3.6.0-rc.1 6.9.4 3.6.0-rc.2 6.9.5 3.6.0-rc.3 7.0.0 3.6.1 7.0.0-beta.1 3.6.2 7.0.0-beta.2 3.6.3 7.0.0-beta.3 3.6.4 7.0.0-rc.1 3.6.5 7.0.0-rc.2 3.6.6 7.0.1 3.6.7 7.0.2 3.7.0 7.1.0 3.7.0-beta.1 7.1.0-beta.1 3.7.0-rc.1 7.1.0-beta.2 3.7.0-rc.2 7.1.0-rc.1 3.7.1 7.1.0-rc.2 3.7.2 7.1.1
woocommerce / src / Admin / Features / Fulfillments / Providers / DPDShippingProvider.php
woocommerce / src / Admin / Features / Fulfillments / Providers Last commit date
ACSCourierShippingProvider.php 3 months ago AbstractShippingProvider.php 3 months ago AmazonLogisticsShippingProvider.php 3 months ago AnPostShippingProvider.php 3 months ago ArasKargoShippingProvider.php 3 months ago AustraliaPostShippingProvider.php 3 months ago AzerpostShippingProvider.php 3 months ago BartoliniBRTShippingProvider.php 3 months ago BelpochtaShippingProvider.php 3 months ago BpostShippingProvider.php 3 months ago BulgarianPostsShippingProvider.php 3 months ago CDEKShippingProvider.php 3 months ago CTTShippingProvider.php 3 months ago CanadaPostShippingProvider.php 3 months ago CeskaPostaShippingProvider.php 3 months ago ChronopostShippingProvider.php 3 months ago CorreosShippingProvider.php 3 months ago CustomShippingProvider.php 3 months ago CyprusPostShippingProvider.php 3 months ago DHLShippingProvider.php 3 months ago DPDShippingProvider.php 3 months ago DeutschePostShippingProvider.php 3 months ago ELTAShippingProvider.php 3 months ago EcontShippingProvider.php 3 months ago EimskipShippingProvider.php 3 months ago EvriHermesShippingProvider.php 3 months ago FanCourierShippingProvider.php 3 months ago FastwayShippingProvider.php 3 months ago FedExShippingProvider.php 3 months ago GLSShippingProvider.php 3 months ago GenikiTaxydromikiShippingProvider.php 3 months ago HayPostShippingProvider.php 3 months ago HelthjemShippingProvider.php 3 months ago HrvatskaPostaShippingProvider.php 3 months ago InPostShippingProvider.php 3 months ago IslandsposturShippingProvider.php 3 months ago ItellaShippingProvider.php 3 months ago KazpostShippingProvider.php 3 months ago LaPosteColissimoShippingProvider.php 3 months ago LasershipOntracShippingProvider.php 3 months ago LatvijasPastsShippingProvider.php 3 months ago LiechtensteinischePostShippingProvider.php 3 months ago MPLShippingProvider.php 3 months ago MRWShippingProvider.php 3 months ago MagyarPostaShippingProvider.php 3 months ago MakedonskaPostaShippingProvider.php 3 months ago MaltaPostShippingProvider.php 3 months ago MatkahuoltoShippingProvider.php 3 months ago MondialRelayShippingProvider.php 3 months ago NewZealandPostShippingProvider.php 3 months ago NovaPoshtaShippingProvider.php 3 months ago OmnivaShippingProvider.php 3 months ago OsterreichischePostShippingProvider.php 3 months ago ParcelForceShippingProvider.php 3 months ago PocztaPolskaShippingProvider.php 3 months ago PostLuxembourgShippingProvider.php 3 months ago PostNLShippingProvider.php 3 months ago PostNordShippingProvider.php 3 months ago PostaMoldoveiShippingProvider.php 3 months ago PostaRomanaShippingProvider.php 3 months ago PosteItalianeShippingProvider.php 3 months ago PosteSanMarinoShippingProvider.php 3 months ago PostenNorgeBringShippingProvider.php 3 months ago PurolatorShippingProvider.php 3 months ago RoyalMailShippingProvider.php 3 months ago RussianPostShippingProvider.php 3 months ago SDAShippingProvider.php 3 months ago SeurShippingProvider.php 3 months ago SlovenskaPostaShippingProvider.php 3 months ago SpeeDeeDeliveryShippingProvider.php 3 months ago StarTrackShippingProvider.php 3 months ago SwissPostShippingProvider.php 3 months ago TollShippingProvider.php 3 months ago UPSShippingProvider.php 3 months ago USPSShippingProvider.php 3 months ago UkrposhtaShippingProvider.php 3 months ago UrgentCargusShippingProvider.php 3 months ago YurticiKargoShippingProvider.php 3 months ago ZasilkovnaShippingProvider.php 3 months ago
DPDShippingProvider.php
468 lines
1 <?php declare(strict_types=1);
2
3 namespace Automattic\WooCommerce\Admin\Features\Fulfillments\Providers;
4
5 /**
6 * DPD Shipping Provider class.
7 *
8 * Provides DPD tracking number validation, supported countries, and tracking URL generation.
9 */
10 class DPDShippingProvider extends AbstractShippingProvider {
11
12 /**
13 * DPD tracking number patterns by country with service differentiation.
14 *
15 * @var array<string, array{patterns: array<int, string>, confidence: int, services?: array<string, int>}>
16 */
17 private const TRACKING_PATTERNS = array(
18 'DE' => array( // Germany.
19 'patterns' => array(
20 '/^\d{14}$/',
21 '/^\d{12}$/',
22 '/^02\d{12}$/', // DPD Classic.
23 '/^05\d{12}$/', // DPD Express.
24 '/^09\d{12}$/', // DPD Predict.
25 '/^[A-Z]{2}\d{9}[A-Z]{2}$/', // S10/UPU international.
26 '/^\d{24}$/', // 24-digit fallback.
27 ),
28 'confidence' => 80,
29 'services' => array(
30 'classic' => 80,
31 'express' => 85,
32 'predict' => 85,
33 's10' => 90,
34 ),
35 ),
36 'GB' => array( // United Kingdom.
37 'patterns' => array(
38 '/^\d{14}$/',
39 '/^[A-Z]{2}\d{9}GB$/',
40 '/^03\d{12}$/', // DPD Next Day.
41 '/^06\d{12}$/', // DPD Express.
42 '/^1[56]\d{12}$/', // Predict/Return.
43 '/^[A-Z]{2}\d{9}[A-Z]{2}$/', // S10/UPU international.
44 '/^\d{24}$/', // 24-digit fallback.
45 ),
46 'confidence' => 90,
47 'services' => array(
48 'next_day' => 88,
49 'express' => 88,
50 's10' => 90,
51 ),
52 ),
53 'FR' => array( // France.
54 'patterns' => array(
55 '/^\d{14}$/',
56 '/^\d{12}$/',
57 '/^02\d{12}$/', // DPD Relais.
58 '/^04\d{12}$/', // DPD Predict.
59 '/^[A-Z]{2}\d{9}[A-Z]{2}$/', // S10/UPU international.
60 '/^\d{24}$/', // 24-digit fallback.
61 ),
62 'confidence' => 78,
63 'services' => array(
64 'relais' => 82,
65 'predict' => 82,
66 's10' => 90,
67 ),
68 ),
69 'NL' => array( // Netherlands.
70 'patterns' => array(
71 '/^\d{14}$/',
72 '/^\d{12}$/',
73 '/^03\d{12}$/', // DPD Classic.
74 '/^07\d{12}$/', // DPD Express.
75 '/^[A-Z]{2}\d{9}[A-Z]{2}$/', // S10/UPU international.
76 '/^\d{24}$/', // 24-digit fallback.
77 ),
78 'confidence' => 78,
79 'services' => array(
80 'classic' => 82,
81 'express' => 85,
82 's10' => 90,
83 ),
84 ),
85 'BE' => array( // Belgium.
86 'patterns' => array(
87 '/^\d{14}$/',
88 '/^\d{12}$/',
89 '/^03\d{12}$/', // DPD Classic.
90 '/^08\d{12}$/', // DPD Express.
91 '/^[A-Z]{2}\d{9}[A-Z]{2}$/', // S10/UPU international.
92 '/^\d{24}$/', // 24-digit fallback.
93 ),
94 'confidence' => 78,
95 'services' => array(
96 'classic' => 82,
97 'express' => 85,
98 's10' => 90,
99 ),
100 ),
101 'PL' => array( // Poland.
102 'patterns' => array(
103 '/^\d{14}$/',
104 '/^[A-Z]{2}\d{10}$/',
105 '/^[A-Z]{2}\d{9}[A-Z]{2}$/', // S10/UPU international.
106 '/^\d{24}$/', // 24-digit fallback.
107 ),
108 'confidence' => 90,
109 ),
110 'IE' => array( // Ireland.
111 'patterns' => array(
112 '/^\d{14}$/',
113 '/^[A-Z]{2}\d{9}IE$/',
114 ),
115 'confidence' => 85,
116 ),
117 'AT' => array( // Austria.
118 'patterns' => array(
119 '/^\d{14}$/',
120 '/^\d{12}$/',
121 ),
122 'confidence' => 75, // Reduced: generic patterns.
123 ),
124 'CH' => array( // Switzerland.
125 'patterns' => array(
126 '/^\d{14}$/',
127 '/^[A-Z]{2}\d{9}CH$/',
128 ),
129 'confidence' => 85,
130 ),
131 'ES' => array( // Spain.
132 'patterns' => array(
133 '/^\d{14}$/',
134 '/^[A-Z]{2}\d{10}$/',
135 ),
136 'confidence' => 85,
137 ),
138 'IT' => array( // Italy.
139 'patterns' => array(
140 '/^\d{14}$/',
141 '/^[A-Z]{2}\d{10}$/',
142 ),
143 'confidence' => 85,
144 ),
145 'LU' => array( // Luxembourg.
146 'patterns' => array(
147 '/^\d{14}$/',
148 '/^\d{12}$/',
149 ),
150 'confidence' => 75, // Reduced: generic patterns.
151 ),
152 'CZ' => array( // Czech Republic.
153 'patterns' => array(
154 '/^\d{14}$/',
155 '/^[A-Z]{2}\d{10}$/',
156 ),
157 'confidence' => 90,
158 ),
159 'SK' => array( // Slovakia.
160 'patterns' => array(
161 '/^\d{14}$/',
162 '/^[A-Z]{2}\d{10}$/',
163 ),
164 'confidence' => 90,
165 ),
166 'HU' => array( // Hungary.
167 'patterns' => array(
168 '/^\d{14}$/',
169 '/^[A-Z]{2}\d{10}$/',
170 ),
171 'confidence' => 90,
172 ),
173 'SI' => array( // Slovenia.
174 'patterns' => array(
175 '/^\d{14}$/',
176 '/^[A-Z]{2}\d{10}$/',
177 ),
178 'confidence' => 80,
179 ),
180 'HR' => array( // Croatia.
181 'patterns' => array(
182 '/^\d{14}$/',
183 '/^[A-Z]{2}\d{10}$/',
184 ),
185 'confidence' => 80,
186 ),
187 'RO' => array( // Romania.
188 'patterns' => array(
189 '/^\d{14}$/',
190 '/^[A-Z]{2}\d{10}$/',
191 ),
192 'confidence' => 75,
193 ),
194 'BG' => array( // Bulgaria.
195 'patterns' => array(
196 '/^\d{14}$/',
197 '/^[A-Z]{2}\d{10}$/',
198 ),
199 'confidence' => 70,
200 ),
201 'LT' => array( // Lithuania.
202 'patterns' => array(
203 '/^\d{14}$/',
204 '/^\d{12}$/',
205 ),
206 'confidence' => 70, // Reduced: generic patterns, limited DPD presence.
207 ),
208 'LV' => array( // Latvia.
209 'patterns' => array(
210 '/^\d{14}$/',
211 '/^\d{12}$/',
212 ),
213 'confidence' => 70, // Reduced: generic patterns, limited DPD presence.
214 ),
215 'EE' => array( // Estonia.
216 'patterns' => array(
217 '/^\d{14}$/',
218 '/^\d{12}$/',
219 ),
220 'confidence' => 70, // Reduced: generic patterns, limited DPD presence.
221 ),
222 'FI' => array( // Finland.
223 'patterns' => array(
224 '/^\d{14}$/',
225 '/^\d{12}$/',
226 ),
227 'confidence' => 65, // Reduced: partnership-based, not direct DPD.
228 ),
229 'DK' => array( // Denmark.
230 'patterns' => array(
231 '/^\d{14}$/',
232 '/^\d{12}$/',
233 ),
234 'confidence' => 65, // Reduced: partnership-based, not direct DPD.
235 ),
236 'SE' => array( // Sweden.
237 'patterns' => array(
238 '/^\d{14}$/',
239 '/^\d{12}$/',
240 ),
241 'confidence' => 65, // Reduced: partnership-based, not direct DPD.
242 ),
243 'NO' => array( // Norway.
244 'patterns' => array(
245 '/^\d{14}$/',
246 '/^\d{12}$/',
247 ),
248 'confidence' => 60, // Reduced: limited DPD presence.
249 ),
250 'GR' => array( // Greece.
251 'patterns' => array(
252 '/^\d{14}$/',
253 '/^[A-Z]{2}\d{10}$/',
254 ),
255 'confidence' => 85,
256 ),
257 'PT' => array( // Portugal.
258 'patterns' => array(
259 '/^\d{14}$/',
260 '/^[A-Z]{2}\d{10}$/',
261 ),
262 'confidence' => 85,
263 ),
264 );
265
266 /**
267 * International shipment pattern (28 digits)
268 */
269 private const INTERNATIONAL_PATTERN = '/^\d{28}$/';
270
271 /**
272 * S10/UPU international pattern.
273 */
274 private const S10_PATTERN = '/^[A-Z]{2}\d{9}[A-Z]{2}$/';
275
276 /**
277 * Get the unique key for this shipping provider.
278 *
279 * @return string Unique key.
280 */
281 public function get_key(): string {
282 return 'dpd';
283 }
284
285 /**
286 * Get the name of this shipping provider.
287 *
288 * @return string Name of the shipping provider.
289 */
290 public function get_name(): string {
291 return 'DPD';
292 }
293
294 /**
295 * Get the icon URL for this shipping provider.
296 *
297 * @return string URL of the shipping provider icon.
298 */
299 public function get_icon(): string {
300 return esc_url( WC()->plugin_url() ) . '/assets/images/shipping_providers/dpd.png';
301 }
302
303 /**
304 * Get the description of this shipping provider.
305 *
306 * @return array Description of the shipping provider.
307 */
308 public function get_shipping_from_countries(): array {
309 return array_keys( self::TRACKING_PATTERNS );
310 }
311
312 /**
313 * Get the countries this shipping provider can ship to.
314 *
315 * DPD typically ships within Europe, so we return the same countries as shipping from.
316 *
317 * @return array List of country codes.
318 */
319 public function get_shipping_to_countries(): array {
320 return $this->get_shipping_from_countries();
321 }
322
323 /**
324 * Get the tracking URL for a given tracking number.
325 *
326 * @param string $tracking_number The tracking number to generate the URL for.
327 * @return string The tracking URL.
328 */
329 public function get_tracking_url( string $tracking_number ): string {
330 return 'https://www.dpd.com/tracking/' . rawurlencode( $tracking_number );
331 }
332
333 /**
334 * Validate tracking number against country-specific patterns and determine service type.
335 *
336 * @param string $tracking_number The tracking number to validate.
337 * @param string $country_code The country code for the shipment.
338 * @return array|bool Array with service info if valid, false otherwise.
339 */
340 private function validate_country_pattern( string $tracking_number, string $country_code ) {
341 if ( ! isset( self::TRACKING_PATTERNS[ $country_code ] ) ) {
342 return false;
343 }
344
345 $country_data = self::TRACKING_PATTERNS[ $country_code ];
346 $detected_service = null;
347 $confidence_boost = 0;
348
349 // Check service-specific patterns first.
350 if ( isset( $country_data['services'] ) ) {
351 if ( preg_match( '/^02\d{12}$/', $tracking_number ) ) {
352 $detected_service = 'classic';
353 $confidence_boost = $country_data['services']['classic'] ?? 0;
354 } elseif ( preg_match( '/^0[34578]\d{12}$/', $tracking_number ) ) {
355 $detected_service = 'express';
356 $confidence_boost = $country_data['services']['express'] ?? 0;
357 } elseif ( preg_match( '/^0[49]\d{12}$/', $tracking_number ) ) {
358 $detected_service = 'predict';
359 $confidence_boost = $country_data['services']['predict'] ?? 0;
360 } elseif ( preg_match( '/^03\d{12}$/', $tracking_number ) && 'GB' === $country_code ) {
361 $detected_service = 'next_day';
362 $confidence_boost = $country_data['services']['next_day'] ?? 0;
363 } elseif ( preg_match( '/^02\d{12}$/', $tracking_number ) && 'FR' === $country_code ) {
364 $detected_service = 'relais';
365 $confidence_boost = $country_data['services']['relais'] ?? 0;
366 } elseif ( preg_match( self::S10_PATTERN, $tracking_number ) ) {
367 $detected_service = 's10';
368 $confidence_boost = $country_data['services']['s10'] ?? 90;
369 }
370 }
371
372 // Check all patterns.
373 foreach ( $country_data['patterns'] as $pattern ) {
374 if ( preg_match( $pattern, $tracking_number ) ) {
375 return array(
376 'valid' => true,
377 'service' => $detected_service,
378 'confidence_boost' => $confidence_boost,
379 );
380 }
381 }
382
383 return false;
384 }
385
386 /**
387 * Try to parse a DPD tracking number.
388 *
389 * @param string $tracking_number The tracking number to parse.
390 * @param string $shipping_from The country code of the shipping origin.
391 * @param string $shipping_to The country code of the shipping destination.
392 * @return array|null An array with 'url' and 'ambiguity_score' if valid, null otherwise.
393 */
394 public function try_parse_tracking_number(
395 string $tracking_number,
396 string $shipping_from,
397 string $shipping_to
398 ): ?array {
399 if ( empty( $tracking_number ) || empty( $shipping_from ) || empty( $shipping_to ) ) {
400 return null;
401 }
402
403 $normalized = strtoupper( preg_replace( '/\s+/', '', $tracking_number ) );
404 if ( empty( $normalized ) ) {
405 return null;
406 }
407
408 $shipping_from = strtoupper( $shipping_from );
409 $shipping_to = strtoupper( $shipping_to );
410
411 // 1. Check international 28-digit format first.
412 if ( preg_match( self::INTERNATIONAL_PATTERN, $normalized ) ) {
413 if ( in_array( $shipping_from, $this->get_shipping_from_countries(), true ) &&
414 in_array( $shipping_to, $this->get_shipping_to_countries(), true ) ) {
415 return array(
416 'url' => $this->get_tracking_url( $normalized ),
417 'ambiguity_score' => 95,
418 );
419 }
420 return null;
421 }
422
423 // 2. Check S10/UPU format (international DPD).
424 if ( preg_match( self::S10_PATTERN, $normalized ) ) {
425 return array(
426 'url' => $this->get_tracking_url( $normalized ),
427 'ambiguity_score' => 90,
428 );
429 }
430
431 // 3. Check country-specific patterns.
432 $validation_result = $this->validate_country_pattern( $normalized, $shipping_from );
433 if ( $validation_result && is_array( $validation_result ) ) {
434 $confidence = self::TRACKING_PATTERNS[ $shipping_from ]['confidence'];
435
436 // Apply service-specific confidence boost.
437 if ( $validation_result['confidence_boost'] > 0 ) {
438 $confidence = min( 95, $validation_result['confidence_boost'] );
439 }
440
441 // Boost confidence for intra-DPD shipments.
442 if ( in_array( $shipping_to, $this->get_shipping_to_countries(), true ) ) {
443 $confidence = min( 98, $confidence + 3 );
444 }
445
446 // Additional boost for express services.
447 if ( 'express' === $validation_result['service'] ) {
448 $confidence = min( 98, $confidence + 2 );
449 }
450
451 return array(
452 'url' => $this->get_tracking_url( $normalized ),
453 'ambiguity_score' => $confidence,
454 );
455 }
456
457 // 4. Fallback: 12–24 digit numeric.
458 if ( preg_match( '/^\d{12,24}$/', $normalized ) ) {
459 return array(
460 'url' => $this->get_tracking_url( $normalized ),
461 'ambiguity_score' => 60,
462 );
463 }
464
465 return null;
466 }
467 }
468