AfterShip – WooCommerce Tracking - Version 1.9.15

Version Description

  • Add new api endpoint for orders sync
Download this release

Release Info

Developer aftership
Plugin Icon 128x128 AfterShip – WooCommerce Tracking
Version 1.9.15
Comparing to
See all releases

Code changes from version 1.9.14 to 1.9.15

aftership.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: AfterShip - WooCommerce Tracking
4
  Plugin URI: http://aftership.com/
5
  Description: Add tracking number and carrier name to WooCommerce, display tracking info at order history page, auto import tracking numbers to AfterShip.
6
- Version: 1.9.14
7
  Author: AfterShip
8
  Author URI: http://aftership.com
9
 
3
  Plugin Name: AfterShip - WooCommerce Tracking
4
  Plugin URI: http://aftership.com/
5
  Description: Add tracking number and carrier name to WooCommerce, display tracking info at order history page, auto import tracking numbers to AfterShip.
6
+ Version: 1.9.15
7
  Author: AfterShip
8
  Author URI: http://aftership.com
9
 
api/class-aftership-api-authentication.php CHANGED
@@ -76,7 +76,7 @@ class AfterShip_API_Authentication
76
  $key = 'AFTERSHIP_WP_KEY';
77
  $key1 = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', $key))));
78
  $key2 = 'AFTERSHIP-WP-KEY';
79
- $qskey = $_GET['key'];
80
 
81
  // get aftership wp key
82
  if (!empty($headers[$key])) {
76
  $key = 'AFTERSHIP_WP_KEY';
77
  $key1 = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', $key))));
78
  $key2 = 'AFTERSHIP-WP-KEY';
79
+ $qskey = isset($_GET['key']) ? $_GET['key'] : null;
80
 
81
  // get aftership wp key
82
  if (!empty($headers[$key])) {
api/class-aftership-api-server.php CHANGED
@@ -135,18 +135,27 @@ class AfterShip_API_Server
135
  }
136
 
137
  // determine type of request/response and load handler, JSON by default
138
- if ($this->is_json_request())
139
  $handler_class = 'AfterShip_API_JSON_Handler';
140
-
141
- elseif ($this->is_xml_request())
142
  $handler_class = 'WC_API_XML_Handler';
143
-
144
- else
145
  $handler_class = apply_filters('aftership_api_default_response_handler', 'AfterShip_API_JSON_Handler', $this->path, $this);
146
-
 
 
 
147
  $this->handler = new $handler_class();
148
  }
149
 
 
 
 
 
 
 
 
 
150
  /**
151
  * Check authentication for the request
152
  *
@@ -429,11 +438,12 @@ class AfterShip_API_Server
429
  'description' => get_option('blogdescription'),
430
  'URL' => get_option('siteurl'),
431
  'wc_version' => WC()->version,
 
432
  'routes' => array(),
433
  'meta' => array(
434
  'timezone' => wc_timezone_string(),
435
- 'currency' => get_aftership_currency(),
436
- 'currency_format' => get_aftership_currency_symbol(),
437
  'tax_included' => ('yes' === get_option('aftership_prices_include_tax')),
438
  'weight_unit' => get_option('aftership_weight_unit'),
439
  'dimension_unit' => get_option('aftership_dimension_unit'),
@@ -466,7 +476,7 @@ class AfterShip_API_Server
466
  // For non-variable routes, generate links
467
  if (strpos($route, '<') === false) {
468
  $data['meta'] = array(
469
- 'self' => get_aftership_api_url($route),
470
  );
471
  }
472
  }
@@ -755,4 +765,267 @@ class AfterShip_API_Server
755
 
756
  return false;
757
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
758
  }
135
  }
136
 
137
  // determine type of request/response and load handler, JSON by default
138
+ if ($this->is_json_request()){
139
  $handler_class = 'AfterShip_API_JSON_Handler';
140
+ }elseif ($this->is_xml_request()){
 
141
  $handler_class = 'WC_API_XML_Handler';
142
+ }else{
 
143
  $handler_class = apply_filters('aftership_api_default_response_handler', 'AfterShip_API_JSON_Handler', $this->path, $this);
144
+ }
145
+ if ($this->is_version_two()) {
146
+ $handler_class = 'AfterShip_API_V2_JSON_Handler';
147
+ }
148
  $this->handler = new $handler_class();
149
  }
150
 
151
+ public function is_version_two() {
152
+ // check path
153
+ if ( false !== stripos( $this->path, '/v2' ) ) {
154
+ return true;
155
+ }
156
+ return false;
157
+ }
158
+
159
  /**
160
  * Check authentication for the request
161
  *
438
  'description' => get_option('blogdescription'),
439
  'URL' => get_option('siteurl'),
440
  'wc_version' => WC()->version,
441
+ 'latest_api_version' => 'v2',
442
  'routes' => array(),
443
  'meta' => array(
444
  'timezone' => wc_timezone_string(),
445
+ 'currency' => get_woocommerce_currency(),
446
+ 'currency_format' => get_woocommerce_currency_symbol(),
447
  'tax_included' => ('yes' === get_option('aftership_prices_include_tax')),
448
  'weight_unit' => get_option('aftership_weight_unit'),
449
  'dimension_unit' => get_option('aftership_dimension_unit'),
476
  // For non-variable routes, generate links
477
  if (strpos($route, '<') === false) {
478
  $data['meta'] = array(
479
+ 'self' => $route,
480
  );
481
  }
482
  }
765
 
766
  return false;
767
  }
768
+
769
+ /**
770
+ * Converts the WooCommerce country codes to 3-letter ISO codes
771
+ * https://en.wikipedia.org/wiki/ISO_3166-1_alpha-3
772
+ * @param string WooCommerce's 2 letter country code
773
+ * @return string ISO 3-letter country code
774
+ */
775
+ public function convert_country_code($country) {
776
+ $countries = array(
777
+ 'AF' => 'AFG', //Afghanistan
778
+ 'AX' => 'ALA', //&#197;land Islands
779
+ 'AL' => 'ALB', //Albania
780
+ 'DZ' => 'DZA', //Algeria
781
+ 'AS' => 'ASM', //American Samoa
782
+ 'AD' => 'AND', //Andorra
783
+ 'AO' => 'AGO', //Angola
784
+ 'AI' => 'AIA', //Anguilla
785
+ 'AQ' => 'ATA', //Antarctica
786
+ 'AG' => 'ATG', //Antigua and Barbuda
787
+ 'AR' => 'ARG', //Argentina
788
+ 'AM' => 'ARM', //Armenia
789
+ 'AW' => 'ABW', //Aruba
790
+ 'AU' => 'AUS', //Australia
791
+ 'AT' => 'AUT', //Austria
792
+ 'AZ' => 'AZE', //Azerbaijan
793
+ 'BS' => 'BHS', //Bahamas
794
+ 'BH' => 'BHR', //Bahrain
795
+ 'BD' => 'BGD', //Bangladesh
796
+ 'BB' => 'BRB', //Barbados
797
+ 'BY' => 'BLR', //Belarus
798
+ 'BE' => 'BEL', //Belgium
799
+ 'BZ' => 'BLZ', //Belize
800
+ 'BJ' => 'BEN', //Benin
801
+ 'BM' => 'BMU', //Bermuda
802
+ 'BT' => 'BTN', //Bhutan
803
+ 'BO' => 'BOL', //Bolivia
804
+ 'BQ' => 'BES', //Bonaire, Saint Estatius and Saba
805
+ 'BA' => 'BIH', //Bosnia and Herzegovina
806
+ 'BW' => 'BWA', //Botswana
807
+ 'BV' => 'BVT', //Bouvet Islands
808
+ 'BR' => 'BRA', //Brazil
809
+ 'IO' => 'IOT', //British Indian Ocean Territory
810
+ 'BN' => 'BRN', //Brunei
811
+ 'BG' => 'BGR', //Bulgaria
812
+ 'BF' => 'BFA', //Burkina Faso
813
+ 'BI' => 'BDI', //Burundi
814
+ 'KH' => 'KHM', //Cambodia
815
+ 'CM' => 'CMR', //Cameroon
816
+ 'CA' => 'CAN', //Canada
817
+ 'CV' => 'CPV', //Cape Verde
818
+ 'KY' => 'CYM', //Cayman Islands
819
+ 'CF' => 'CAF', //Central African Republic
820
+ 'TD' => 'TCD', //Chad
821
+ 'CL' => 'CHL', //Chile
822
+ 'CN' => 'CHN', //China
823
+ 'CX' => 'CXR', //Christmas Island
824
+ 'CC' => 'CCK', //Cocos (Keeling) Islands
825
+ 'CO' => 'COL', //Colombia
826
+ 'KM' => 'COM', //Comoros
827
+ 'CG' => 'COG', //Congo
828
+ 'CD' => 'COD', //Congo, Democratic Republic of the
829
+ 'CK' => 'COK', //Cook Islands
830
+ 'CR' => 'CRI', //Costa Rica
831
+ 'CI' => 'CIV', //Côte d\'Ivoire
832
+ 'HR' => 'HRV', //Croatia
833
+ 'CU' => 'CUB', //Cuba
834
+ 'CW' => 'CUW', //Curaçao
835
+ 'CY' => 'CYP', //Cyprus
836
+ 'CZ' => 'CZE', //Czech Republic
837
+ 'DK' => 'DNK', //Denmark
838
+ 'DJ' => 'DJI', //Djibouti
839
+ 'DM' => 'DMA', //Dominica
840
+ 'DO' => 'DOM', //Dominican Republic
841
+ 'EC' => 'ECU', //Ecuador
842
+ 'EG' => 'EGY', //Egypt
843
+ 'SV' => 'SLV', //El Salvador
844
+ 'GQ' => 'GNQ', //Equatorial Guinea
845
+ 'ER' => 'ERI', //Eritrea
846
+ 'EE' => 'EST', //Estonia
847
+ 'ET' => 'ETH', //Ethiopia
848
+ 'FK' => 'FLK', //Falkland Islands
849
+ 'FO' => 'FRO', //Faroe Islands
850
+ 'FJ' => 'FIJ', //Fiji
851
+ 'FI' => 'FIN', //Finland
852
+ 'FR' => 'FRA', //France
853
+ 'GF' => 'GUF', //French Guiana
854
+ 'PF' => 'PYF', //French Polynesia
855
+ 'TF' => 'ATF', //French Southern Territories
856
+ 'GA' => 'GAB', //Gabon
857
+ 'GM' => 'GMB', //Gambia
858
+ 'GE' => 'GEO', //Georgia
859
+ 'DE' => 'DEU', //Germany
860
+ 'GH' => 'GHA', //Ghana
861
+ 'GI' => 'GIB', //Gibraltar
862
+ 'GR' => 'GRC', //Greece
863
+ 'GL' => 'GRL', //Greenland
864
+ 'GD' => 'GRD', //Grenada
865
+ 'GP' => 'GLP', //Guadeloupe
866
+ 'GU' => 'GUM', //Guam
867
+ 'GT' => 'GTM', //Guatemala
868
+ 'GG' => 'GGY', //Guernsey
869
+ 'GN' => 'GIN', //Guinea
870
+ 'GW' => 'GNB', //Guinea-Bissau
871
+ 'GY' => 'GUY', //Guyana
872
+ 'HT' => 'HTI', //Haiti
873
+ 'HM' => 'HMD', //Heard Island and McDonald Islands
874
+ 'VA' => 'VAT', //Holy See (Vatican City State)
875
+ 'HN' => 'HND', //Honduras
876
+ 'HK' => 'HKG', //Hong Kong
877
+ 'HU' => 'HUN', //Hungary
878
+ 'IS' => 'ISL', //Iceland
879
+ 'IN' => 'IND', //India
880
+ 'ID' => 'IDN', //Indonesia
881
+ 'IR' => 'IRN', //Iran
882
+ 'IQ' => 'IRQ', //Iraq
883
+ 'IE' => 'IRL', //Republic of Ireland
884
+ 'IM' => 'IMN', //Isle of Man
885
+ 'IL' => 'ISR', //Israel
886
+ 'IT' => 'ITA', //Italy
887
+ 'JM' => 'JAM', //Jamaica
888
+ 'JP' => 'JPN', //Japan
889
+ 'JE' => 'JEY', //Jersey
890
+ 'JO' => 'JOR', //Jordan
891
+ 'KZ' => 'KAZ', //Kazakhstan
892
+ 'KE' => 'KEN', //Kenya
893
+ 'KI' => 'KIR', //Kiribati
894
+ 'KP' => 'PRK', //Korea, Democratic People\'s Republic of
895
+ 'KR' => 'KOR', //Korea, Republic of (South)
896
+ 'KW' => 'KWT', //Kuwait
897
+ 'KG' => 'KGZ', //Kyrgyzstan
898
+ 'LA' => 'LAO', //Laos
899
+ 'LV' => 'LVA', //Latvia
900
+ 'LB' => 'LBN', //Lebanon
901
+ 'LS' => 'LSO', //Lesotho
902
+ 'LR' => 'LBR', //Liberia
903
+ 'LY' => 'LBY', //Libya
904
+ 'LI' => 'LIE', //Liechtenstein
905
+ 'LT' => 'LTU', //Lithuania
906
+ 'LU' => 'LUX', //Luxembourg
907
+ 'MO' => 'MAC', //Macao S.A.R., China
908
+ 'MK' => 'MKD', //Macedonia
909
+ 'MG' => 'MDG', //Madagascar
910
+ 'MW' => 'MWI', //Malawi
911
+ 'MY' => 'MYS', //Malaysia
912
+ 'MV' => 'MDV', //Maldives
913
+ 'ML' => 'MLI', //Mali
914
+ 'MT' => 'MLT', //Malta
915
+ 'MH' => 'MHL', //Marshall Islands
916
+ 'MQ' => 'MTQ', //Martinique
917
+ 'MR' => 'MRT', //Mauritania
918
+ 'MU' => 'MUS', //Mauritius
919
+ 'YT' => 'MYT', //Mayotte
920
+ 'MX' => 'MEX', //Mexico
921
+ 'FM' => 'FSM', //Micronesia
922
+ 'MD' => 'MDA', //Moldova
923
+ 'MC' => 'MCO', //Monaco
924
+ 'MN' => 'MNG', //Mongolia
925
+ 'ME' => 'MNE', //Montenegro
926
+ 'MS' => 'MSR', //Montserrat
927
+ 'MA' => 'MAR', //Morocco
928
+ 'MZ' => 'MOZ', //Mozambique
929
+ 'MM' => 'MMR', //Myanmar
930
+ 'NA' => 'NAM', //Namibia
931
+ 'NR' => 'NRU', //Nauru
932
+ 'NP' => 'NPL', //Nepal
933
+ 'NL' => 'NLD', //Netherlands
934
+ 'AN' => 'ANT', //Netherlands Antilles
935
+ 'NC' => 'NCL', //New Caledonia
936
+ 'NZ' => 'NZL', //New Zealand
937
+ 'NI' => 'NIC', //Nicaragua
938
+ 'NE' => 'NER', //Niger
939
+ 'NG' => 'NGA', //Nigeria
940
+ 'NU' => 'NIU', //Niue
941
+ 'NF' => 'NFK', //Norfolk Island
942
+ 'MP' => 'MNP', //Northern Mariana Islands
943
+ 'NO' => 'NOR', //Norway
944
+ 'OM' => 'OMN', //Oman
945
+ 'PK' => 'PAK', //Pakistan
946
+ 'PW' => 'PLW', //Palau
947
+ 'PS' => 'PSE', //Palestinian Territory
948
+ 'PA' => 'PAN', //Panama
949
+ 'PG' => 'PNG', //Papua New Guinea
950
+ 'PY' => 'PRY', //Paraguay
951
+ 'PE' => 'PER', //Peru
952
+ 'PH' => 'PHL', //Philippines
953
+ 'PN' => 'PCN', //Pitcairn
954
+ 'PL' => 'POL', //Poland
955
+ 'PT' => 'PRT', //Portugal
956
+ 'PR' => 'PRI', //Puerto Rico
957
+ 'QA' => 'QAT', //Qatar
958
+ 'RE' => 'REU', //Reunion
959
+ 'RO' => 'ROU', //Romania
960
+ 'RU' => 'RUS', //Russia
961
+ 'RW' => 'RWA', //Rwanda
962
+ 'BL' => 'BLM', //Saint Barth&eacute;lemy
963
+ 'SH' => 'SHN', //Saint Helena
964
+ 'KN' => 'KNA', //Saint Kitts and Nevis
965
+ 'LC' => 'LCA', //Saint Lucia
966
+ 'MF' => 'MAF', //Saint Martin (French part)
967
+ 'SX' => 'SXM', //Sint Maarten / Saint Matin (Dutch part)
968
+ 'PM' => 'SPM', //Saint Pierre and Miquelon
969
+ 'VC' => 'VCT', //Saint Vincent and the Grenadines
970
+ 'WS' => 'WSM', //Samoa
971
+ 'SM' => 'SMR', //San Marino
972
+ 'ST' => 'STP', //S&atilde;o Tom&eacute; and Pr&iacute;ncipe
973
+ 'SA' => 'SAU', //Saudi Arabia
974
+ 'SN' => 'SEN', //Senegal
975
+ 'RS' => 'SRB', //Serbia
976
+ 'SC' => 'SYC', //Seychelles
977
+ 'SL' => 'SLE', //Sierra Leone
978
+ 'SG' => 'SGP', //Singapore
979
+ 'SK' => 'SVK', //Slovakia
980
+ 'SI' => 'SVN', //Slovenia
981
+ 'SB' => 'SLB', //Solomon Islands
982
+ 'SO' => 'SOM', //Somalia
983
+ 'ZA' => 'ZAF', //South Africa
984
+ 'GS' => 'SGS', //South Georgia/Sandwich Islands
985
+ 'SS' => 'SSD', //South Sudan
986
+ 'ES' => 'ESP', //Spain
987
+ 'LK' => 'LKA', //Sri Lanka
988
+ 'SD' => 'SDN', //Sudan
989
+ 'SR' => 'SUR', //Suriname
990
+ 'SJ' => 'SJM', //Svalbard and Jan Mayen
991
+ 'SZ' => 'SWZ', //Swaziland
992
+ 'SE' => 'SWE', //Sweden
993
+ 'CH' => 'CHE', //Switzerland
994
+ 'SY' => 'SYR', //Syria
995
+ 'TW' => 'TWN', //Taiwan
996
+ 'TJ' => 'TJK', //Tajikistan
997
+ 'TZ' => 'TZA', //Tanzania
998
+ 'TH' => 'THA', //Thailand
999
+ 'TL' => 'TLS', //Timor-Leste
1000
+ 'TG' => 'TGO', //Togo
1001
+ 'TK' => 'TKL', //Tokelau
1002
+ 'TO' => 'TON', //Tonga
1003
+ 'TT' => 'TTO', //Trinidad and Tobago
1004
+ 'TN' => 'TUN', //Tunisia
1005
+ 'TR' => 'TUR', //Turkey
1006
+ 'TM' => 'TKM', //Turkmenistan
1007
+ 'TC' => 'TCA', //Turks and Caicos Islands
1008
+ 'TV' => 'TUV', //Tuvalu
1009
+ 'UG' => 'UGA', //Uganda
1010
+ 'UA' => 'UKR', //Ukraine
1011
+ 'AE' => 'ARE', //United Arab Emirates
1012
+ 'GB' => 'GBR', //United Kingdom
1013
+ 'US' => 'USA', //United States
1014
+ 'UM' => 'UMI', //United States Minor Outlying Islands
1015
+ 'UY' => 'URY', //Uruguay
1016
+ 'UZ' => 'UZB', //Uzbekistan
1017
+ 'VU' => 'VUT', //Vanuatu
1018
+ 'VE' => 'VEN', //Venezuela
1019
+ 'VN' => 'VNM', //Vietnam
1020
+ 'VG' => 'VGB', //Virgin Islands, British
1021
+ 'VI' => 'VIR', //Virgin Island, U.S.
1022
+ 'WF' => 'WLF', //Wallis and Futuna
1023
+ 'EH' => 'ESH', //Western Sahara
1024
+ 'YE' => 'YEM', //Yemen
1025
+ 'ZM' => 'ZMB', //Zambia
1026
+ 'ZW' => 'ZWE', //Zimbabwe
1027
+ );
1028
+ $iso_code = isset($countries[$country]) ? $countries[$country] : $country;
1029
+ return $iso_code;
1030
+ }
1031
  }
api/class-aftership-api-v2-json-handler.php ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * AfterShip API
4
+ *
5
+ * Handles parsing JSON request bodies and generating JSON responses
6
+ *
7
+ * @author AfterShip
8
+ * @category API
9
+ * @package AfterShip/API
10
+ * @since 1.0
11
+ */
12
+
13
+ if (!defined('ABSPATH')) {
14
+ exit;
15
+ } // Exit if accessed directly
16
+
17
+ class AfterShip_API_V2_JSON_Handler implements AfterShip_API_Handler
18
+ {
19
+ /**
20
+ * Get the content type for the response
21
+ *
22
+ * @return string
23
+ * @since 2.1
24
+ */
25
+ public function get_content_type()
26
+ {
27
+ return 'application/json; charset=' . get_option('blog_charset');
28
+ }
29
+
30
+ /**
31
+ * Parse the raw request body entity
32
+ *
33
+ * @param string $body the raw request body
34
+ *
35
+ * @return array|mixed
36
+ * @since 2.1
37
+ *
38
+ */
39
+ public function parse_body($body)
40
+ {
41
+ return json_decode($body, true);
42
+ }
43
+
44
+ /**
45
+ * Generate a JSON response given an array of data
46
+ *
47
+ * @param array $data the response data
48
+ *
49
+ * @return string
50
+ * @since 2.1
51
+ *
52
+ */
53
+ public function generate_response($data)
54
+ {
55
+ if (isset($_GET['_jsonp'])) {
56
+ // JSONP enabled by default
57
+ if (!apply_filters('aftership_api_jsonp_enabled', true)) {
58
+
59
+ WC()->api->server->send_status(400);
60
+
61
+ $data = [
62
+ [
63
+ 'code' => 'aftership_api_jsonp_disabled',
64
+ 'message' => __('JSONP support is disabled on this site', 'aftership')
65
+ ]
66
+ ];
67
+ }
68
+
69
+ // Check for invalid characters (only alphanumeric allowed)
70
+ if (preg_match('/\W/', $_GET['_jsonp'])) {
71
+
72
+ WC()->api->server->send_status(400);
73
+
74
+ $data = [
75
+ [
76
+ 'code' => 'aftership_api_jsonp_callback_invalid',
77
+ __('The JSONP callback function is invalid', 'aftership')
78
+ ]
79
+ ];
80
+ }
81
+
82
+ return $_GET['_jsonp'] . '(' . json_encode($data) . ')';
83
+ }
84
+
85
+ $ok = [
86
+ 'meta' => [
87
+ 'code' => 20000,
88
+ 'type' => 'OK',
89
+ 'message' => 'Everything worked as expected'
90
+ ],
91
+ 'data' => $data
92
+ ];
93
+
94
+ if (isset($data['errors'])) {
95
+ $error = [
96
+ 'meta' => [
97
+ 'code' => $this->map_error_code($data['errors'][0]['code']),
98
+ 'type' => $data['errors'][0]['code'],
99
+ 'message' => $data['errors'][0]['message'],
100
+
101
+ ],
102
+ 'data' => '{}'
103
+ ];
104
+ return json_encode($error);
105
+ }
106
+
107
+
108
+ return json_encode($ok);
109
+ }
110
+
111
+ /**
112
+ * get error code by error message
113
+ * @param $code_text
114
+ * @return int
115
+ */
116
+ private function map_error_code($code_text)
117
+ {
118
+ $code = 40010;
119
+ switch ($code_text) {
120
+ case 'aftership_api_disabled':
121
+ $code = 400011;
122
+ break;
123
+ default:
124
+ break;
125
+ }
126
+
127
+ return $code;
128
+ }
129
+ }
api/class-aftership-api-v2-orders.php ADDED
@@ -0,0 +1,420 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * AfterShip API Orders Class
4
+ *
5
+ * Handles requests to the /orders endpoint
6
+ *
7
+ * @author AfterShip
8
+ * @category API
9
+ * @package AfterShip/API
10
+ * @since 1.0
11
+ */
12
+
13
+ if (!defined('ABSPATH')) {
14
+ exit;
15
+ } // Exit if accessed directly
16
+
17
+ class AfterShip_API_V2_Orders extends AfterShip_API_Resource
18
+ {
19
+
20
+ /** @var string $base the route base */
21
+ protected $base = '/v2/orders';
22
+
23
+ /**
24
+ * Register the routes for this class
25
+ *
26
+ * GET /orders
27
+ *
28
+ * @param array $routes
29
+ *
30
+ * @return array
31
+ * @since 2.1
32
+ *
33
+ */
34
+ public function register_routes($routes)
35
+ {
36
+ # GET /orders/ping
37
+ $routes[$this->base . '/ping'] = [
38
+ [[$this, 'ping'], AfterShip_API_Server::READABLE],
39
+ ];
40
+
41
+ # GET /orders
42
+ $routes[$this->base] = [
43
+ [[$this, 'get_orders'], AfterShip_API_Server::READABLE]
44
+ ];
45
+
46
+ # GET /orders/:id
47
+ $routes[$this->base . '/(?P<id>[\d]+)'] = [
48
+ [[$this, 'get_order'], AfterShip_API_Server::READABLE],
49
+ ];
50
+
51
+ return $routes;
52
+ }
53
+
54
+ /**
55
+ * heath checkendpoint for wordpress url validation
56
+ *
57
+ * @return string
58
+ * @since 2.1
59
+ */
60
+ public function ping()
61
+ {
62
+ return 'pong';
63
+ }
64
+
65
+ /**
66
+ * Get orders
67
+ *
68
+ * @param string $updated_at_min
69
+ * @param string $updated_at_max
70
+ * @param string $max_results_number
71
+ *
72
+ * @return array
73
+ * @throws Exception
74
+ * @since 2.1
75
+ *
76
+ */
77
+ public function get_orders($updated_at_min = null, $updated_at_max = null, $max_results_number = null)
78
+ {
79
+ $args = [
80
+ 'updated_at_min' => $updated_at_min,
81
+ 'updated_at_max' => $updated_at_max,
82
+ 'orderby' => 'modified',
83
+ 'order' => 'ASC',
84
+ 'limit' => $max_results_number,
85
+ 'page' => !empty($_GET['page']) && intval($_GET['page']) > 1 ? absint($_GET['page']) : 1
86
+ ];
87
+
88
+ $query = $this->query_orders($args);
89
+
90
+ //define pagination
91
+ $pagination = [
92
+ 'page' => $query->query['paged'],
93
+ 'limit' => intval($query->query['posts_per_page']),
94
+ 'total' => intval($query->found_posts)
95
+ ];
96
+
97
+ $orders = [];
98
+ foreach ($query->posts as $order_id) {
99
+ if (!$this->is_readable($order_id)) {
100
+ continue;
101
+ }
102
+ $orders[] = $this->get_order($order_id);
103
+ }
104
+
105
+ return ['orders' => $orders, 'pagination' => $pagination];
106
+ }
107
+
108
+ /**
109
+ * get single order by id
110
+ * @param $id
111
+ * @return array|int|WP_Error
112
+ * @throws Exception
113
+ */
114
+ public function get_order($id)
115
+ {
116
+ $weight_unit = get_option('woocommerce_weight_unit');
117
+ // ensure order ID is valid & user has permission to read
118
+ $id = $this->validate_request($id, 'shop_order', 'read');
119
+ if (is_wp_error($id)) {
120
+ return $id;
121
+ }
122
+ $order = new WC_Order($id);
123
+ $customer = new WC_Customer($order->get_customer_id());
124
+ $shipping_method = current($order->get_shipping_methods());
125
+ $order_data = [
126
+ 'id' => (string)$order->get_id(),
127
+ 'order_number' => $order->get_order_number(),
128
+ 'order_name' => $order->get_order_number(),
129
+ 'taxes_included' => ($order->get_total_tax() > 0),
130
+ 'shipping_method' => [
131
+ 'code' => $shipping_method['method_id'],
132
+ 'name' => $shipping_method['name'],
133
+ ],
134
+ 'order_total' => [
135
+ 'currency' => $order->get_currency(),
136
+ 'amount' => (float)wc_format_decimal($order->get_total(), 2),
137
+ ],
138
+ 'note' => $order->get_customer_note(),
139
+ 'locale' => get_locale(),
140
+ 'metrics' => [
141
+ 'placed_at' => $this->server->format_datetime($order->get_date_created()),
142
+ 'updated_at' => $this->server->format_datetime($order->get_date_modified()),
143
+ 'fully_shipped_at' => null,
144
+ 'expected_earliest_delivery_at' => null,
145
+ 'expected_last_delivery_at' => null,
146
+ ],
147
+ 'customer' => [
148
+ 'id' => (string)$order->get_customer_id(),
149
+ 'first_name' => $customer->get_first_name(),
150
+ 'last_name' => $customer->get_last_name(),
151
+ 'emails' => ($customer->get_email()) ? [$customer->get_email()] : [],
152
+ 'phones' => ($customer->get_billing_phone()) ? [[
153
+ 'country_code' => null,
154
+ 'number' => $customer->get_billing_phone()
155
+ ]] : [],
156
+ ],
157
+ 'shipping_address' => [
158
+ 'first_name' => $order->get_shipping_first_name(),
159
+ 'last_name' => $order->get_shipping_last_name(),
160
+ 'company' => $order->get_shipping_company(),
161
+ 'address_line_1' => $order->get_shipping_address_1(),
162
+ 'address_line_2' => $order->get_shipping_address_2(),
163
+ 'city' => $order->get_shipping_city(),
164
+ 'state' => $order->get_shipping_state(),
165
+ 'country' => $this->server->convert_country_code($order->get_shipping_country()),
166
+ 'postal_code' => $order->get_shipping_postcode(),
167
+ 'email' => $order->get_billing_email(),
168
+ 'phone' => [
169
+ 'country_code' => null,
170
+ 'number' => $order->get_billing_phone()
171
+ ],
172
+ 'address_type' => null,
173
+ 'tax_number' => null,
174
+ ],
175
+ 'billing_address' => array(
176
+ 'first_name' => $order->get_billing_first_name(),
177
+ 'last_name' => $order->get_billing_last_name(),
178
+ 'company' => $order->get_billing_company(),
179
+ 'address_line_1' => $order->get_billing_address_1(),
180
+ 'address_line_2' => $order->get_billing_address_2(),
181
+ 'city' => $order->get_billing_city(),
182
+ 'state' => $order->get_billing_state(),
183
+ 'postal_code' => $order->get_billing_postcode(),
184
+ 'country' => $this->server->convert_country_code($order->get_billing_country()),
185
+ 'email' => $order->get_billing_email(),
186
+ 'phone' => [
187
+ 'country_code' => null,
188
+ 'number' => $order->get_billing_phone()
189
+ ],
190
+ 'address_type' => null,
191
+ 'tax_number' => null,
192
+ ),
193
+ 'status' => $order->get_status(),
194
+ 'items' => [],
195
+ 'trackings' => []
196
+ ];
197
+
198
+
199
+ // add line items
200
+ foreach ($order->get_items() as $item_id => $item) {
201
+ if (is_callable($item, 'get_product')) {
202
+ $product = $item->get_product();
203
+ } else {
204
+ $product = $order->get_product_from_item($item);
205
+ }
206
+ if (empty($product)) continue;
207
+ $weight = $product->get_weight();
208
+ $product_id = (isset($product->variation_id)) ? $product->variation_id : $product->get_id();
209
+ // set the response object
210
+ $terms_tags = get_the_terms($product_id, 'product_tag');
211
+ $product_tags = [];
212
+ foreach ($terms_tags as $termsKey => $termsVal) {
213
+ $product_tags[] = $termsVal->name;
214
+ }
215
+ $product_categories = [];
216
+
217
+ $categories = get_the_terms($product_id, 'product_cat');
218
+ foreach ($categories as $categoriesKey => $categoriesVal) {
219
+ $product_categories[] = $categoriesVal->name;
220
+ }
221
+ $order_data['items'][] = [
222
+ 'id' => $item_id,
223
+ 'product_id' => $product_id,
224
+ 'sku' => is_object($product) ? $product->get_sku() : null,
225
+ 'title' => $item['name'],
226
+ 'quantity' => (int)$item['qty'],
227
+ 'returnable_quantity' => (int)($item['qty'] - $order->get_qty_refunded_for_item($item_id)),
228
+
229
+ 'unit_weight' => [
230
+ 'unit' => $weight_unit,
231
+ 'value' => (float)$weight,
232
+ ],
233
+ 'unit_price' => [
234
+ 'currency' => $order->get_currency(),
235
+ 'amount' => (float)wc_format_decimal($order->get_item_total($item), 2),
236
+ ],
237
+ 'discount' => null,
238
+ 'image_urls' => [wp_get_attachment_url($product->image_id)],
239
+ 'tags' => $product_tags,
240
+ 'categories' => $product_categories,
241
+ ];
242
+ }
243
+
244
+ // tracking field will be
245
+ /*
246
+ {
247
+ tracking_number: fulfillment.tracking_number,
248
+ slug: mapped_slug,
249
+ additional_fields: {
250
+ account_number: null,
251
+ key: null,
252
+ postal_code: (data.shipping_address && data.shipping_address.zip) ? data.shipping_address.zip : null,
253
+ ship_date: moment(fulfillment.updated_at).utcOffset('+0000').format('YYYYMMDD'),
254
+ state: null,
255
+ origin_country: null,
256
+ destination_country: (data.shipping_address && data.shipping_address.country_code) ? beautifyAddress({country: data.shipping_address.country_code}).country_iso3 : null,
257
+ },
258
+ }
259
+ */
260
+
261
+ $trackings = [];
262
+ //The function definition will be available after installing the aftership plugin.
263
+ if(function_exists('order_post_meta_getter')) {
264
+ $aftership_tracking_number = order_post_meta_getter($order, 'aftership_tracking_number');
265
+ if (!empty($aftership_tracking_number)) {
266
+ $trackings[] = [
267
+ 'slug' => order_post_meta_getter($order, 'aftership_tracking_provider'),
268
+ 'tracking_number' => $aftership_tracking_number,
269
+ 'additional_fields' => [
270
+ 'postal_code' => order_post_meta_getter($order, 'aftership_tracking_postal'),
271
+ ],
272
+ ];
273
+ }
274
+
275
+ // 兼容 woocommerce 官方的 tracking 插件
276
+ $woocommerce_tracking_arr = order_post_meta_getter($order, 'wc_shipment_tracking_items');
277
+ if (empty($aftership_tracking_number) && !empty($woocommerce_tracking_arr)) {
278
+ foreach ($woocommerce_tracking_arr as $trackingKey => $trackingVal) {
279
+ $trackingArr = $this->getTrackingInfoByShipmentTracking($trackingVal);
280
+ if (!empty($trackingArr)) {
281
+ $trackings[] = [
282
+ 'slug' => $trackingArr['tracking_provider'],
283
+ 'tracking_number' => $trackingVal["tracking_number"],
284
+ 'additional_fields' => [
285
+ 'postal_code' => $trackingArr['tracking_postal_code'],
286
+ ],
287
+ ];
288
+ } else {
289
+ $trackings[] = [
290
+ 'slug' => $trackingVal["tracking_provider"],
291
+ 'tracking_number' =>$trackingVal["tracking_number"],
292
+ 'additional_fields'=> [
293
+ 'postal_code' => null,
294
+ ]
295
+ ];
296
+ }
297
+ }
298
+ }
299
+ $order_data['trackings'] = $trackings;
300
+ }
301
+
302
+ return $order_data;
303
+ }
304
+
305
+ /**
306
+ * 从wc ShipmentTracking 插件获取 Postalcode - postnl
307
+ * @param $tracking_items
308
+ * @return array
309
+ */
310
+ private function getTrackingInfoByShipmentTracking($tracking_items)
311
+ {
312
+ if (!isset($tracking_items['custom_tracking_link'])) {
313
+ return array();
314
+ }
315
+
316
+ // 获取 postnl Postalcode
317
+ $urlArr = parse_url(stripslashes($tracking_items['custom_tracking_link']));
318
+
319
+ if ($urlArr === false) {
320
+ return array();
321
+ }
322
+
323
+ if (!isset($urlArr['host'])) {
324
+ return array();
325
+ }
326
+
327
+ $hostArr = explode(".", $urlArr['host']);
328
+ $hostArrIndex = count($hostArr) - 2;
329
+ if (empty($hostArr) || !isset($hostArr[$hostArrIndex])) {
330
+ return array();
331
+ }
332
+
333
+ if ($hostArr[$hostArrIndex] == 'postnl') {
334
+ parse_str($urlArr['query'], $queryArr);
335
+ if (!isset($queryArr['Postalcode'])) {
336
+ return array();
337
+ }
338
+
339
+ return array(
340
+ 'tracking_provider' => 'postnl',
341
+ 'tracking_postal_code' => str_replace(" ", "", $queryArr['Postalcode']),
342
+ );
343
+ }
344
+ return array();
345
+ }
346
+
347
+
348
+ /**
349
+ * Helper method to get order post objects
350
+ *
351
+ * @param array $args request arguments for filtering query
352
+ *
353
+ * @return WP_Query
354
+ * @since 2.1
355
+ *
356
+ */
357
+ private function query_orders($args)
358
+ {
359
+
360
+ function aftership_wpbo_get_woo_version_number()
361
+ {
362
+ // If get_plugins() isn't available, require it
363
+ if (!function_exists('get_plugins'))
364
+ require_once(ABSPATH . 'wp-admin/includes/plugin.php');
365
+
366
+ // Create the plugins folder and file variables
367
+ $plugin_folder = get_plugins('/' . 'woocommerce');
368
+ $plugin_file = 'woocommerce.php';
369
+
370
+ // If the plugin version number is set, return it
371
+ if (isset($plugin_folder[$plugin_file]['Version'])) {
372
+ return $plugin_folder[$plugin_file]['Version'];
373
+
374
+ } else {
375
+ // Otherwise return null
376
+ return NULL;
377
+ }
378
+ }
379
+
380
+ $woo_version = aftership_wpbo_get_woo_version_number();
381
+
382
+ if ($woo_version >= 2.2) {
383
+ // set base query arguments
384
+ $query_args = array(
385
+ 'fields' => 'ids',
386
+ 'post_type' => 'shop_order',
387
+ // 'post_status' => 'publish',
388
+ 'post_status' => array_keys(wc_get_order_statuses())
389
+ );
390
+ } else {
391
+ // set base query arguments
392
+ $query_args = array(
393
+ 'fields' => 'ids',
394
+ 'post_type' => 'shop_order',
395
+ 'post_status' => 'publish',
396
+ );
397
+ }
398
+
399
+ // add status argument
400
+ if (!empty($args['status'])) {
401
+
402
+ $statuses = explode(',', $args['status']);
403
+
404
+ $query_args['tax_query'] = array(
405
+ array(
406
+ 'taxonomy' => 'shop_order_status',
407
+ 'field' => 'slug',
408
+ 'terms' => $statuses,
409
+ ),
410
+ );
411
+
412
+ unset($args['status']);
413
+ }
414
+
415
+ $query_args = $this->merge_query_args($query_args, $args);
416
+
417
+ return new WP_Query($query_args);
418
+ }
419
+
420
+ }
class-aftership-api.php CHANGED
@@ -32,6 +32,8 @@ class AfterShip_API
32
  */
33
  public function __construct()
34
  {
 
 
35
 
36
  // add query vars
37
  add_filter('query_vars', array($this, 'add_query_vars'), 0);
@@ -148,6 +150,7 @@ class AfterShip_API
148
  include_once('api/class-aftership-api-server.php');
149
  include_once('api/interface-aftership-api-handler.php');
150
  include_once('api/class-aftership-api-json-handler.php');
 
151
 
152
  // authentication
153
  include_once('api/class-aftership-api-authentication.php');
@@ -157,6 +160,8 @@ class AfterShip_API
157
 
158
  // self api
159
  include_once('api/class-aftership-api-orders.php');
 
 
160
  }
161
 
162
  /**
@@ -171,6 +176,7 @@ class AfterShip_API
171
  $api_classes = apply_filters('aftership_api_classes',
172
  array(
173
  'AfterShip_API_Orders',
 
174
  )
175
  );
176
 
32
  */
33
  public function __construct()
34
  {
35
+ // disable notice output in api json response
36
+ error_reporting(0);
37
 
38
  // add query vars
39
  add_filter('query_vars', array($this, 'add_query_vars'), 0);
150
  include_once('api/class-aftership-api-server.php');
151
  include_once('api/interface-aftership-api-handler.php');
152
  include_once('api/class-aftership-api-json-handler.php');
153
+ include_once('api/class-aftership-api-v2-json-handler.php');
154
 
155
  // authentication
156
  include_once('api/class-aftership-api-authentication.php');
160
 
161
  // self api
162
  include_once('api/class-aftership-api-orders.php');
163
+ include_once('api/class-aftership-api-v2-orders.php');
164
+
165
  }
166
 
167
  /**
176
  $api_classes = apply_filters('aftership_api_classes',
177
  array(
178
  'AfterShip_API_Orders',
179
+ 'AfterShip_API_V2_Orders',
180
  )
181
  );
182
 
readme.txt CHANGED
@@ -4,7 +4,7 @@ Donate link: https://www.aftership.com/
4
  Tags: shipping, tracking, ups, usps, fedex, dhl, tnt, dpd, post, shipment, woocommerce, tracking number, aftership, package tracking, fulfilment, tracking link, carrier, courier, woo commerce, woocommerce shipment tracking, shipping details plugin, widget, shipstation, track, package
5
  Requires at least: 2.9
6
  Tested up to: 5.2.1
7
- Stable tag: 1.9.14
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
@@ -97,6 +97,9 @@ You'll find the FAQ on [AfterShip.com](https://aftership.uservoice.com/knowledge
97
 
98
  == Changelog ==
99
 
 
 
 
100
  = 1.9.14 =
101
  * Add new couriers
102
 
4
  Tags: shipping, tracking, ups, usps, fedex, dhl, tnt, dpd, post, shipment, woocommerce, tracking number, aftership, package tracking, fulfilment, tracking link, carrier, courier, woo commerce, woocommerce shipment tracking, shipping details plugin, widget, shipstation, track, package
5
  Requires at least: 2.9
6
  Tested up to: 5.2.1
7
+ Stable tag: 1.9.15
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
97
 
98
  == Changelog ==
99
 
100
+ = 1.9.15 =
101
+ * Add new api endpoint for orders sync
102
+
103
  = 1.9.14 =
104
  * Add new couriers
105