array(
'title' => esc_html__( 'Manual', 'postnl-for-woocommerce' ),
'type' => 'title',
// translators: %1$s & %2$s is replaced with tag.
'description' => sprintf( __( 'Consult the %1$smanual%2$s for help installing the plug-in.', 'postnl-for-woocommerce' ), '', '' ),
),
// Account Settings.
'account_settings_title' => array(
'title' => esc_html__( 'Account Settings', 'postnl-for-woocommerce' ),
'type' => 'title',
// translators: %1$s & %2$s is replaced with tag.
'description' => sprintf( __( 'Please configure your shipping parameters and your access towards the PostNL APIs by means of authentication. You can find the details of your PostNL account in Mijn %1$sPostNL%2$s under "My Account > API beheren".', 'postnl-for-woocommerce' ), '', '' ),
),
'environment_mode' => array(
'title' => esc_html__( 'Environment Mode', 'postnl-for-woocommerce' ),
'type' => 'select',
'description' => __( 'Choose the environment mode.', 'postnl-for-woocommerce' ),
'desc_tip' => true,
'options' => array(
'production' => esc_html__( 'Production', 'postnl-for-woocommerce' ),
'sandbox' => esc_html__( 'Sandbox', 'postnl-for-woocommerce' ),
),
'class' => 'wc-enhanced-select',
'default' => 'production',
'placeholder' => '',
),
// Legacy production key. Renamed to "Old API Key" and only rendered when
// a value is already stored (see the gating in get_setting_fields()); a
// fresh install shows the "API Key" field alone. Kept read-only so
// merchants enter their key in the new field ahead of the API migration.
'api_keys' => array(
'title' => esc_html__( 'Old API Key', 'postnl-for-woocommerce' ),
'type' => 'text',
'description' => esc_html__( 'The key you were using before. It is locked because new keys go in the field below.', 'postnl-for-woocommerce' ),
'desc_tip' => false,
'default' => '',
'placeholder' => '',
'custom_attributes' => array( 'readonly' => 'readonly' ),
),
'api_keys_new' => array(
'title' => esc_html__( 'API Key', 'postnl-for-woocommerce' ),
'type' => 'text',
'description' => esc_html__( 'Enter your PostNL API key here. You can get it from the Self Service module on the PostNL Business Portal.', 'postnl-for-woocommerce' ),
'desc_tip' => false,
'default' => '',
'placeholder' => '',
),
'api_keys_new_status' => array(
'type' => 'postnl_new_key_status',
'environment' => 'production',
),
'api_keys_sandbox' => array(
'title' => esc_html__( 'Old Sandbox API Key', 'postnl-for-woocommerce' ),
'type' => 'text',
'description' => esc_html__( 'The sandbox key you were using before. It is locked because new keys go in the field below.', 'postnl-for-woocommerce' ),
'desc_tip' => false,
'default' => '',
'placeholder' => '',
'custom_attributes' => array( 'readonly' => 'readonly' ),
),
'api_keys_sandbox_new' => array(
'title' => esc_html__( 'Sandbox API Key', 'postnl-for-woocommerce' ),
'type' => 'text',
'description' => esc_html__( 'Enter your PostNL sandbox API key here. You can get it from the Self Service module on the PostNL Business Portal.', 'postnl-for-woocommerce' ),
'desc_tip' => false,
'default' => '',
'placeholder' => '',
),
'api_keys_sandbox_new_status' => array(
'type' => 'postnl_new_key_status',
'environment' => 'sandbox',
),
'enable_logging' => array(
'title' => esc_html__( 'Logging', 'postnl-for-woocommerce' ),
'type' => 'checkbox',
'description' => sprintf(
// translators: %1$s is anchor opener tag and %2$s is anchor closer tag.
esc_html__( 'A log file containing the communication to the PostNL server will be maintained if this option is checked. This can be used in case of technical issues and can be found %1$shere%2$s.', 'postnl-for-woocommerce' ),
'',
''
),
'label' => esc_html__( 'Enable', 'postnl-for-woocommerce' ),
'desc_tip' => false,
'default' => '',
'placeholder' => '',
),
'customer_num' => array(
'title' => esc_html__( 'Customer Number', 'postnl-for-woocommerce' ),
'type' => 'text',
'description' => esc_html__( 'e.g. "11223344"', 'postnl-for-woocommerce' ),
'desc_tip' => true,
'default' => '',
'placeholder' => '11223344',
),
'customer_code' => array(
'title' => esc_html__( 'Customer Code', 'postnl-for-woocommerce' ),
'type' => 'text',
'description' => esc_html__( 'e.g. "DEVC"', 'postnl-for-woocommerce' ),
'desc_tip' => true,
'default' => '',
'placeholder' => 'DEVC',
'custom_attributes' => array( 'maxlength' => '10' ),
),
'return_company' => array(
'title' => esc_html__( 'Company Name', 'postnl-for-woocommerce' ),
'type' => 'text',
'description' => esc_html__( 'Enter company name - this name will be noted as the sender on the label', 'postnl-for-woocommerce' ),
'desc_tip' => true,
'default' => '',
),
/*
Temporarily hardcoded.
'location_code' => array(
'title' => esc_html__( 'Location Code', 'postnl-for-woocommerce' ),
'type' => 'text',
'description' => esc_html__( 'e.g. "123456"', 'postnl-for-woocommerce' ),
'desc_tip' => true,
'default' => '',
'placeholder' => '123456',
'custom_attributes' => array( 'maxlength' => '10' ),
),
*/
// Return Settings.
'return_settings_title' => array(
'title' => esc_html__( 'Return Settings', 'postnl-for-woocommerce' ),
'type' => 'title',
// 'description' => esc_html__( 'If you have a reply number, only fill in the Zip code, City and Return code. If you want to return your shipments to a home address, also provide the address line (Street, Housenumber and HouseNrExt) of your return address.', 'postnl-for-woocommerce' ),
),
'return_shipment_and_labels' => array(
'title' => esc_html__( 'Standard return option', 'postnl-for-woocommerce' ),
'type' => 'select',
'description' => esc_html__( '- None: return labels are not automatically created', 'postnl-for-woocommerce' ) . '
' .
esc_html__( '- Label in the box: a separate return label is created at the same time as the label for the outward shipment and can be included in the box.', 'postnl-for-woocommerce' ),
'options' => array(
'none' => esc_html__( 'None', 'postnl-for-woocommerce' ),
'in_box' => esc_html__( 'Label in the box', 'postnl-for-woocommerce' ),
),
'for_country' => array( 'NL' ),
),
// 'return_address_default' => array(
// 'title' => esc_html__( 'Always print returnlabel together with shipping label', 'postnl-for-woocommerce' ),
// 'type' => 'checkbox',
// 'description' => esc_html__( 'With this setting enabled, the return-label of a shipment will automatically be downloaded and printed when the shipping label created.', 'postnl-for-woocommerce' ),
// 'desc_tip' => true,
// 'default' => '',
// 'label' => esc_html__( 'Enable', 'postnl-for-woocommerce' ),
// 'placeholder' => '',
// ),
'return_shipment_and_labels_all' => array(
'title' => esc_html__( 'Directly activate return function for all labels', 'postnl-for-woocommerce' ),
'type' => 'select',
'label' => esc_html__( 'Enable', 'postnl-for-woocommerce' ),
// 'description' => esc_html__( 'Tick this box if you want all labels to be activated for returning immediately. If you do not tick this box the return function can be activated on an order-by-order basis.', 'postnl-for-woocommerce' ),
'options' => array(
'yes' => esc_html__( 'Yes, activate return function directly for all orders', 'postnl-for-woocommerce' ),
'no' => esc_html__( 'No, activate return function per individual order', 'postnl-for-woocommerce' ),
),
'for_country' => array( 'NL' ),
),
'activate_smart_return' => array(
'title' => esc_html__( 'Activate Smart Return', 'postnl-for-woocommerce' ),
'type' => 'checkbox',
'default' => '',
'label' => esc_html__( 'Activate', 'postnl-for-woocommerce' ),
'placeholder' => '',
),
'return_address_or_reply_no' => array(
'title' => esc_html__( 'Return to home address', 'postnl-for-woocommerce' ),
'type' => 'checkbox',
'label' => esc_html__( 'Activate', 'postnl-for-woocommerce' ),
'description' => esc_html__( 'Activate this setting to use a home address for return shipments', 'postnl-for-woocommerce' ),
'desc_tip' => true,
),
'return_replynumber' => array(
'title' => esc_html__( 'Replynumber', 'postnl-for-woocommerce' ),
'type' => 'text',
'description' => esc_html__( 'Enter replynumber.', 'postnl-for-woocommerce' ),
'desc_tip' => true,
'default' => '',
'for_country' => array( 'NL' ),
'class' => 'country-nl',
),
'freepost_zip' => array(
'title' => esc_html__( 'Freepost Zipcode', 'postnl-for-woocommerce' ),
'type' => 'text',
'description' => esc_html__( 'Enter Freepost Zipcode.', 'postnl-for-woocommerce' ),
'desc_tip' => true,
'default' => '',
),
'freepost_city' => array(
'title' => esc_html__( 'Freepost City', 'postnl-for-woocommerce' ),
'type' => 'text',
'description' => esc_html__( 'Enter Freepost City.', 'postnl-for-woocommerce' ),
'desc_tip' => true,
'default' => '',
),
'return_address_street' => array(
'title' => esc_html__( 'Street Address', 'postnl-for-woocommerce' ),
'type' => 'text',
'description' => esc_html__( 'Enter Return Street Address.', 'postnl-for-woocommerce' ),
'desc_tip' => true,
'default' => '',
),
'return_address_house_no' => array(
'title' => esc_html__( 'House Number', 'postnl-for-woocommerce' ),
'type' => 'text',
'description' => esc_html__( 'Enter return house number.', 'postnl-for-woocommerce' ),
'desc_tip' => true,
'default' => '',
),
'return_address_house_noext' => array(
'title' => esc_html__( 'House Number Extension', 'postnl-for-woocommerce' ),
'type' => 'text',
'description' => esc_html__( 'Enter return house number extension.', 'postnl-for-woocommerce' ),
'desc_tip' => true,
'default' => '',
),
'return_address_zip' => array(
'title' => esc_html__( 'Zipcode', 'postnl-for-woocommerce' ),
'type' => 'text',
'description' => esc_html__( 'Enter Return Zipcode.', 'postnl-for-woocommerce' ),
'desc_tip' => true,
'default' => '',
),
'return_address_city' => array(
'title' => esc_html__( 'City', 'postnl-for-woocommerce' ),
'type' => 'text',
'description' => esc_html__( 'Enter Return City.', 'postnl-for-woocommerce' ),
'desc_tip' => true,
'default' => '',
),
'return_customer_code' => array(
'title' => esc_html__( 'Return Customer Code', 'postnl-for-woocommerce' ),
'type' => 'text',
'description' => esc_html__( 'Be aware that the Return Customer Code differs from the regular Customer Code. You can find your Return customer code in Mijn PostNL.', 'postnl-for-woocommerce' ),
'desc_tip' => true,
'default' => '',
),
// Delivery Options Settings.
'delivery_options_title' => array(
'title' => esc_html__( 'Checkout Settings', 'postnl-for-woocommerce' ),
'type' => 'title',
'description' => esc_html__( 'Please configure your checkout preferences.', 'postnl-for-woocommerce' ),
),
'supported_shipping_methods' => array(
'title' => esc_html__( 'Shipping Methods', 'postnl-for-woocommerce' ),
'type' => 'multiselect',
'description' => esc_html__( 'Select Shipping Methods can be associated with PostNL.', 'postnl-for-woocommerce' ),
'desc_tip' => true,
'options' => $this->get_shipping_methods(),
'class' => 'wc-enhanced-select',
),
'letterbox_24_fee' => array(
'title' => esc_html__( 'Extra fee for the letterboxparcel (24 hours)', 'postnl-for-woocommerce' ),
'type' => 'price',
'description' => esc_html__( 'Extra fee added when Letterboxparcel Standard (24 hours) has been selected.', 'postnl-for-woocommerce' ),
'desc_tip' => true,
'for_country' => array( 'NL' ),
'class' => 'wc_input_price country-nl',
),
'letterbox_fee' => array(
'title' => __( 'Letterbox fee', 'postnl-for-woocommerce' ),
'type' => 'price',
'description' => __( 'Overrides the shipping cost when all items are eligible for letterbox delivery. Leave empty to use the standard shipping cost. The fee is set to €0 when free shipping applies.', 'postnl-for-woocommerce' ),
'desc_tip' => true,
'for_country' => array( 'NL' ),
'class' => 'wc_input_price country-nl',
),
'enable_pickup_points' => array(
'title' => __( 'PostNL Pick-up Points', 'postnl-for-woocommerce' ),
'type' => 'checkbox',
'label' => __( 'Enable', 'postnl-for-woocommerce' ),
'description' => __( 'Show PostNL pick-up points in the checkout so that your customers can choose to get their orders delivered at a PostNL pick-up point.', 'postnl-for-woocommerce' ),
'desc_tip' => true,
'default' => 'yes',
'for_country' => array( 'NL', 'BE' ),
'class' => 'country-nl country-be',
),
'pickup_delivery_fee' => array(
'title' => __( 'Extra fee pick-up delivery', 'postnl-for-woocommerce' ),
'type' => 'price',
'description' => __( 'Extra fee added when the customer selects a PostNL pick-up point.', 'postnl-for-woocommerce' ),
'desc_tip' => true,
'for_country' => array( 'NL', 'BE' ),
'class' => 'wc_input_price country-nl country-be',
),
/*
Temporarily commented out.
'number_pickup_points' => array(
'title' => __( 'Number of Pickup Points', 'postnl-for-woocommerce' ),
'type' => 'number',
'description' => __( 'Number of pickup points displayed in the frontend. Maximum will be 20.', 'postnl-for-woocommerce' ),
'desc_tip' => true,
'class' => '',
'default' => '10',
'custom_attributes' => array(
'min' => '1',
'max' => '20',
),
'for_country' => array( 'NL', 'BE' ),
'class' => 'country-nl country-be',
),
*/
'enable_delivery_days' => array(
'title' => __( 'Delivery', 'postnl-for-woocommerce' ),
'type' => 'checkbox',
'label' => __( 'Enable', 'postnl-for-woocommerce' ),
'description' => __( 'Show delivery days in the checkout so that your customers can choose which day to receive their order.', 'postnl-for-woocommerce' ),
'desc_tip' => true,
'default' => '',
'for_country' => array( 'NL' ),
'class' => 'country-nl',
),
'delivery_days_fee' => array(
'title' => __( 'Extra fee home delivery', 'postnl-for-woocommerce' ),
'type' => 'price',
'description' => __( 'Extra fee added when the customer selects home delivery.', 'postnl-for-woocommerce' ),
'desc_tip' => true,
'for_country' => array( 'NL', 'BE' ),
'class' => 'wc_input_price country-nl country-be',
),
'number_delivery_days' => array(
'title' => __( 'Number of Delivery Days', 'postnl-for-woocommerce' ),
'type' => 'number',
'description' => __( 'Number of delivery days displayed in the frontend. Maximum will be 12.', 'postnl-for-woocommerce' ),
'desc_tip' => true,
'default' => '10',
'for_country' => array( 'NL' ),
'custom_attributes' => array(
'min' => '1',
'max' => '12',
),
'class' => 'country-nl',
),
'enable_morning_delivery' => array(
'title' => __( 'Morning Delivery', 'postnl-for-woocommerce' ),
'type' => 'checkbox',
'label' => __( 'Enable', 'postnl-for-woocommerce' ),
'description' => __( 'Enable morning delivery in the checkout so your customers can choose to receive their orders in the morning.', 'postnl-for-woocommerce' ),
'desc_tip' => true,
'default' => '',
'for_country' => array( 'NL' ),
'class' => 'country-nl',
),
'morning_delivery_fee' => array(
'title' => __( 'Morning Delivery Fee', 'postnl-for-woocommerce' ),
'type' => 'price',
'description' => __( 'Fee for receiving orders in the morning.', 'postnl-for-woocommerce' ),
'desc_tip' => true,
'for_country' => array( 'NL' ),
'class' => 'wc_input_price country-nl',
),
'enable_evening_delivery' => array(
'title' => __( 'Evening Delivery', 'postnl-for-woocommerce' ),
'type' => 'checkbox',
'label' => __( 'Enable', 'postnl-for-woocommerce' ),
'description' => __( 'Enable evening delivery in the checkout so your customers can choose to receive their orders in the evening.', 'postnl-for-woocommerce' ),
'desc_tip' => true,
'default' => '',
'for_country' => array( 'NL' ),
'class' => 'country-nl',
),
'evening_delivery_fee' => array(
'title' => __( 'Evening Delivery Fee', 'postnl-for-woocommerce' ),
'type' => 'price',
'description' => __( 'Fee for receiving orders in the evening.', 'postnl-for-woocommerce' ),
'desc_tip' => true,
'for_country' => array( 'NL' ),
'class' => 'wc_input_price country-nl',
),
'default_checkout_tab' => array(
'title' => __( 'Default Delivery Options Tab', 'postnl-for-woocommerce' ),
'type' => 'select',
'description' => __( 'Choose which tab is shown first in the delivery options menu at checkout.', 'postnl-for-woocommerce' ),
'desc_tip' => true,
'default' => 'delivery_day',
'options' => array(
'delivery_day' => __( 'Home Delivery', 'postnl-for-woocommerce' ),
'dropoff_points' => __( 'Pickup Points', 'postnl-for-woocommerce' ),
),
'for_country' => array( 'NL', 'BE' ),
'class' => 'country-nl country-be',
),
'transit_time' => array(
'title' => esc_html__( 'Transit Time', 'postnl-for-woocommerce' ),
'type' => 'number',
'description' => esc_html__( 'The number of days it takes for the order to be delivered after the order has been placed.', 'postnl-for-woocommerce' ),
'desc_tip' => true,
'default' => '1',
'placeholder' => '',
),
'cut_off_time' => array(
'title' => esc_html__( 'Cut Off Time', 'postnl-for-woocommerce' ),
'type' => 'time',
'description' => esc_html__( 'If an order is ordered after this time, one day will be added to the transit time.', 'postnl-for-woocommerce' ),
'desc_tip' => true,
'default' => '18:00',
'placeholder' => '',
),
'dropoff_day_mon' => array(
'title' => __( 'Drop off Days', 'postnl-for-woocommerce' ),
'type' => 'checkbox',
'label' => __( 'Monday', 'postnl-for-woocommerce' ),
'description' => __( 'Select which days orders will be shipped.', 'postnl-for-woocommerce' ),
'default' => 'yes',
'desc_tip' => true,
),
'dropoff_day_tue' => array(
'type' => 'checkbox',
'label' => __( 'Tuesday', 'postnl-for-woocommerce' ),
'default' => 'yes',
),
'dropoff_day_wed' => array(
'type' => 'checkbox',
'label' => __( 'Wednesday', 'postnl-for-woocommerce' ),
'default' => 'yes',
),
'dropoff_day_thu' => array(
'type' => 'checkbox',
'label' => __( 'Thursday', 'postnl-for-woocommerce' ),
'default' => 'yes',
),
'dropoff_day_fri' => array(
'type' => 'checkbox',
'label' => __( 'Friday', 'postnl-for-woocommerce' ),
'default' => 'yes',
),
'dropoff_day_sat' => array(
'type' => 'checkbox',
'label' => __( 'Saturday', 'postnl-for-woocommerce' ),
'default' => 'yes',
),
'dropoff_day_sun' => array(
'type' => 'checkbox',
'label' => __( 'Sunday', 'postnl-for-woocommerce' ),
),
'validate_nl_address' => array(
'title' => __( 'Validate Dutch addresses', 'postnl-for-woocommerce' ),
'type' => 'checkbox',
'label' => __( 'Enable', 'postnl-for-woocommerce' ),
'description' => __( 'Based on zipcode and housenumber combination the address is checked.', 'postnl-for-woocommerce' ),
'desc_tip' => true,
'default' => 'yes',
),
'reorder_nl_address' => array(
'title' => __( 'Use PostNL address-field', 'postnl-for-woocommerce' ),
'type' => 'checkbox',
'label' => __( 'Enable', 'postnl-for-woocommerce' ),
'description' => __( 'For zipcode, housenumber, housenumber extension and street separate address fields are displayed when this settings is enabled. This only applies for Dutch addresses.', 'postnl-for-woocommerce' ),
'desc_tip' => true,
'default' => 'yes',
),
// Shipping Outside Europe Settings.
'shipping_outside_eu_title' => array(
'title' => esc_html__( 'Shipping Outside Europe Settings', 'postnl-for-woocommerce' ),
'type' => 'title',
'description' => esc_html__( 'Please insert your Parcels non-EU credentials.', 'postnl-for-woocommerce' ),
),
'globalpack_barcode_type' => array(
'title' => esc_html__( 'Parcels non-EU Barcode Type', 'postnl-for-woocommerce' ),
'type' => 'text',
'description' => '',
'desc_tip' => true,
'default' => '',
'placeholder' => esc_html__( 'CD', 'postnl-for-woocommerce' ),
'custom_attributes' => array( 'maxlength' => '10' ),
),
'globalpack_customer_code' => array(
'title' => esc_html__( 'Parcels non-EU Customer Code', 'postnl-for-woocommerce' ),
'type' => 'text',
'description' => '',
'desc_tip' => true,
'default' => '',
'placeholder' => esc_html__( '1234', 'postnl-for-woocommerce' ),
'custom_attributes' => array( 'maxlength' => '10' ),
),
'hs_tariff_code' => array(
'title' => esc_html__( 'Default HS Tariff Code', 'postnl-for-woocommerce' ),
'type' => 'text',
'description' => esc_html__( 'The HS tariff code is used by customs to classify goods. The HS tariff code can be found on the website of the Dutch Chamber of Commerce.', 'postnl-for-woocommerce' ),
'desc_tip' => true,
'default' => '',
'placeholder' => '',
),
'country_origin' => array(
'title' => esc_html__( 'Default Country of Origin', 'postnl-for-woocommerce' ),
'type' => 'select',
'description' => esc_html__( 'Default country of origin is used by customs.', 'postnl-for-woocommerce' ),
'desc_tip' => true,
'default' => Utils::get_base_country(),
'options' => WC()->countries->get_countries(),
'placeholder' => '',
),
'merchant_codes_repeater' => array(
'title' => esc_html__( 'Merchant Customs Code', 'postnl-for-woocommerce' ),
'type' => 'repeater',
'description' => esc_html__( 'Add merchant codes for specific non-EU countries.', 'postnl-for-woocommerce' ),
'desc_tip' => true,
'for_country' => array( 'NL', 'BE' ),
),
'printer_email_title' => array(
'title' => esc_html__( 'Printer & Email Settings', 'postnl-for-woocommerce' ),
'type' => 'title',
'description' => esc_html__( 'Please configure your printer and email preferences.', 'postnl-for-woocommerce' ),
),
'printer_type' => array(
'title' => esc_html__( 'Printer Type', 'postnl-for-woocommerce' ),
'type' => 'select',
'description' => esc_html__( 'It is not recommended to send .pdf files/labels directly to a Zebra printer If you want to send it directly to your Zebra printer, please use .gif files or the native (generic) zpl printer type', 'postnl-for-woocommerce' ),
'desc_tip' => true,
'default' => 'PDF',
'options' => array(
'PDF' => 'PDF',
'GIF' => 'GIF',
'JPG' => 'JPG',
'ZPL' => 'ZPL',
),
),
'printer_type_resolution' => array(
'title' => esc_html__( 'DPI', 'postnl-for-woocommerce' ),
'type' => 'select',
'default' => '600',
'options' => array(
'600' => '600',
'300' => '300',
'200' => '200',
),
),
'label_format' => array(
'title' => esc_html__( 'Label Format', 'postnl-for-woocommerce' ),
'type' => 'select',
'description' => esc_html__( 'Use A6 format in case you use a labelprinter. Use A4 format for other regular printers.', 'postnl-for-woocommerce' ),
'desc_tip' => true,
'default' => 'A6',
'options' => array(
'A6' => 'A6',
'A4' => 'A4',
),
'class' => 'wc-enhanced-select',
),
'shipping_confirmation_email' => array(
'title' => esc_html__( 'Shipping confirmation e-mail address', 'postnl-for-woocommerce' ),
'type' => 'email',
'description' => esc_html__( 'This e-mail address will be used by PostNL for shipping confirmations. If left empty, no sender e-mail will be included.', 'postnl-for-woocommerce' ),
'desc_tip' => true,
),
'woocommerce_email' => array(
'title' => esc_html__( 'WooCommerce Email', 'postnl-for-woocommerce' ),
'type' => 'checkbox',
'label' => esc_html__( 'When PostNL label is created send email to customer.', 'postnl-for-woocommerce' ),
'description' => esc_html__( 'When PostNL label is created send email to customer.', 'postnl-for-woocommerce' ),
'desc_tip' => true,
'default' => '',
'placeholder' => '',
),
'woocommerce_email_text' => array(
'title' => esc_html__( 'WooCommerce Email Text', 'postnl-for-woocommerce' ),
'type' => 'text',
'description' => esc_html__( 'Text added for tracking note email.', 'postnl-for-woocommerce' ),
'desc_tip' => true,
'default' => esc_html__( 'This is your track and track link {tracking-link}', 'postnl-for-woocommerce' ),
'placeholder' => esc_html__( 'This is your track and track link {tracking-link}', 'postnl-for-woocommerce' ),
),
// Default shipping Options Settings.
'default_shipping_options_title' => array(
'title' => esc_html__( 'Default shipping Options Settings', 'postnl-for-woocommerce' ),
'type' => 'title',
'description' => esc_html__( 'Please select Default shipping Options.', 'postnl-for-woocommerce' ),
'for_country' => array( 'NL' ),
),
'default_shipping_options_nl' => array(
'title' => __( 'Shipping options domestic', 'postnl-for-woocommerce' ),
'type' => 'select',
'description' => __( 'Select a default shipping option for domestic orders that are shipped with PostNL.', 'postnl-for-woocommerce' ),
'default' => 'standard_shipment',
'for_country' => array( 'NL' ),
'options' => array(
'standard_shipment' => __( 'Standard shipment', 'postnl-for-woocommerce' ),
'id_check' => __( 'ID Check (18+)', 'postnl-for-woocommerce' ),
'id_check|insured_shipping' => __( 'ID Check (18+) + Insured Shipping', 'postnl-for-woocommerce' ),
// 'insured_shipping' => __( 'Insured Shipping', 'postnl-for-woocommerce' ),
'return_no_answer' => __( 'Return if no answer', 'postnl-for-woocommerce' ),
'signature_on_delivery' => __( 'Signature on Delivery', 'postnl-for-woocommerce' ),
'only_home_address' => __( 'Only Home Address', 'postnl-for-woocommerce' ),
'letterbox' => __( 'Letterboxparcel Standard (24 hours)', 'postnl-for-woocommerce' ),
'letterbox_48' => __( 'Letterboxparcel 48 hours', 'postnl-for-woocommerce' ),
'signature_on_delivery|insured_shipping' => __( 'Signature on Delivery + Insured Shipping', 'postnl-for-woocommerce' ),
'signature_on_delivery|return_no_answer' => __( 'Signature on Delivery + Return if no answer', 'postnl-for-woocommerce' ),
'insured_shipping|return_no_answer|signature_on_delivery' => __( 'Insured Shipping + Return if no answer + Signature on Delivery', 'postnl-for-woocommerce' ),
'only_home_address|return_no_answer' => __( 'Only Home Address + Return if no answer', 'postnl-for-woocommerce' ),
'only_home_address|return_no_answer|signature_on_delivery' => __( 'Only Home Address + Return if no answer + Signature on Delivery', 'postnl-for-woocommerce' ),
'only_home_address|signature_on_delivery' => __( 'Only Home Address + Signature on Delivery', 'postnl-for-woocommerce' ),
'delivery_code_at_door|insured_shipping' => esc_html__( 'Delivery Code at Door + Insured Shipping', 'postnl-for-woocommerce' ),
),
),
'default_shipping_options_be' => array(
'title' => __( 'Shipping options Belgium', 'postnl-for-woocommerce' ),
'type' => 'select',
'description' => __( 'Select a default shipping option for the orders shipped to Belgium with PostNL.', 'postnl-for-woocommerce' ),
'default' => 'standard_belgium',
'for_country' => array( 'NL' ),
'options' => array(
'standard_belgium' => __( 'Standard Shipment Belgium', 'postnl-for-woocommerce' ),
'standard_belgium|only_home_address' => __( 'Standard Shipment Belgium + Only Home Address', 'postnl-for-woocommerce' ),
'standard_belgium|signature_on_delivery' => __( 'Standard Shipment Belgium + Signature on Delivery', 'postnl-for-woocommerce' ),
'standard_belgium|insured_shipping' => __( 'Standard Shipment Belgium + Insured Shipping', 'postnl-for-woocommerce' ),
'mailboxpacket' => __( 'Boxable Packet', 'postnl-for-woocommerce' ),
'mailboxpacket|track_and_trace' => __( 'Boxable Packet + Track & Trace', 'postnl-for-woocommerce' ),
'packets' => __( 'Packets', 'postnl-for-woocommerce' ),
'packets|track_and_trace' => __( 'Packets + Track & Trace', 'postnl-for-woocommerce' ),
'packets|track_and_trace|insured_shipping' => __( 'Packets + Track & Trace + Insured', 'postnl-for-woocommerce' ),
),
),
'default_shipping_options_eu' => array(
'title' => __( 'Shipping options EU', 'postnl-for-woocommerce' ),
'type' => 'select',
'description' => __( 'Select a default shipping option for the orders shipped within European Union zone.', 'postnl-for-woocommerce' ),
'default' => 'eu_parcel|track_and_trace',
'for_country' => array( 'NL' ),
'options' => array(
'eu_parcel|track_and_trace' => __( 'EU Parcel + Track & Trace', 'postnl-for-woocommerce' ),
'eu_parcel|track_and_trace|insured_shipping' => __( 'EU Parcel + Track & Trace + Insured', 'postnl-for-woocommerce' ),
'eu_parcel|track_and_trace|insured_plus' => __( 'EU Parcel + Track & Trace + Insured Plus', 'postnl-for-woocommerce' ),
'mailboxpacket' => __( 'Boxable Packet', 'postnl-for-woocommerce' ),
'mailboxpacket|track_and_trace' => __( 'Boxable Packet + Track & Trace', 'postnl-for-woocommerce' ),
'packets' => __( 'Packets', 'postnl-for-woocommerce' ),
'packets|track_and_trace' => __( 'Packets + Track & Trace', 'postnl-for-woocommerce' ),
'packets|track_and_trace|insured_shipping' => __( 'Packets + Track & Trace + Insured', 'postnl-for-woocommerce' ),
),
),
'default_shipping_options_row' => array(
'title' => __( 'Default Shipping International', 'postnl-for-woocommerce' ),
'type' => 'select',
'description' => __( 'Shipping options non-EU (outside the EU borders).', 'postnl-for-woocommerce' ),
'default' => 'parcel_non_eu|track_and_trace',
'for_country' => array( 'NL' ),
'options' => array(
'parcel_non_eu|track_and_trace' => __( 'Parcel non-EU + Track & Trace', 'postnl-for-woocommerce' ),
'parcel_non_eu|track_and_trace|insured_shipping' => __( 'Parcel non-EU + Track & Trace + Insured', 'postnl-for-woocommerce' ),
'parcel_non_eu|track_and_trace|insured_plus' => __( 'Parcel non-EU + Track & Trace + Insured Plus', 'postnl-for-woocommerce' ),
'mailboxpacket' => __( 'Boxable Packet', 'postnl-for-woocommerce' ),
'mailboxpacket|track_and_trace' => __( 'Boxable Packet + Track & Trace', 'postnl-for-woocommerce' ),
'packets' => __( 'Packets', 'postnl-for-woocommerce' ),
'packets|track_and_trace' => __( 'Packets + Track & Trace', 'postnl-for-woocommerce' ),
'packets|track_and_trace|insured_shipping' => __( 'Packets + Track & Trace + Insured', 'postnl-for-woocommerce' ),
),
),
'default_shipping_options_pickup' => array(
'title' => __( 'Default Shipping Pickup', 'postnl-for-woocommerce' ),
'type' => 'select',
'description' => __( 'Shipping options Pickup.', 'postnl-for-woocommerce' ),
'default' => 'id_check',
'for_country' => array( 'NL' ),
'options' => array(
'' => esc_html__( 'Standard Shipping', 'postnl-for-woocommerce' ),
'id_check' => esc_html__( 'ID Check (18+)', 'postnl-for-woocommerce' ),
'insured_shipping' => esc_html__( 'Insured Shipping', 'postnl-for-woocommerce' ),
'id_check|insured_shipping' => esc_html__( 'ID Check (18+) + Insured Shipping', 'postnl-for-woocommerce' ),
),
),
'default_automatic_letterboxparcel_product' => array(
'title' => esc_html__( 'Default automatic letterboxparcel product', 'postnl-for-woocommerce' ),
'type' => 'select',
'description' => '',
'default' => 'letterbox',
'for_country' => array( 'NL' ),
'options' => array(
'letterbox' => esc_html__( 'Letterboxparcel Standard (24 hours)', 'postnl-for-woocommerce' ),
'letterbox_48' => esc_html__( 'Letterboxparcel 48 hours', 'postnl-for-woocommerce' ),
'customer_decide' => esc_html__( 'Let customer decide', 'postnl-for-woocommerce' ),
),
),
'auto_complete_order' => array(
'title' => esc_html__( 'Automatically change order status to Completed', 'postnl-for-woocommerce' ),
'type' => 'checkbox',
'label' => esc_html__( 'Automatically change order status to Completed once an order has been pre-alerted and printed', 'postnl-for-woocommerce' ),
'description' => esc_html__( 'Automatically change order status to Completed once an order has been pre-alerted and printed', 'postnl-for-woocommerce' ),
'desc_tip' => true,
'default' => '',
'placeholder' => '',
),
);
// The legacy key fields are shown only when a key is already stored: a
// fresh install enters its key in the new "API Key" field alone. The
// decision is made on the saved value (not the posted one) so a failed
// save does not make the field flicker in and out. Reading the raw option
// directly avoids recursing through get_country_option()/filter_setting_fields().
if ( '' === trim( (string) $this->get_option( 'api_keys' ) ) ) {
unset( $fields['api_keys'] );
}
if ( '' === trim( (string) $this->get_option( 'api_keys_sandbox' ) ) ) {
unset( $fields['api_keys_sandbox'] );
}
return $fields;
}
/**
* Filter the setting fields based on store country.
*
* @param String $country Two characters country code.
* @param bool $only_field_country Flag to check if it only return for the field with 'for_country' array exists.
*
* @return array
*/
public function filter_setting_fields( $country, $only_field_country = false, $settings = false ) {
$setting_fields = $this->get_setting_fields();
if ( $country == 'BE' && ! $settings ) {
$setting_fields['default_shipping_options_title'] =
array(
'title' => esc_html__( 'Default shipping Options Settings', 'postnl-for-woocommerce' ),
'type' => 'title',
'description' => esc_html__( 'Please select Default shipping Options.', 'postnl-for-woocommerce' ),
'for_country' => array( 'BE' ),
);
$setting_fields['default_shipping_options_row'] =
array(
'title' => __( 'Default Shipping International', 'postnl-for-woocommerce' ),
'type' => 'select',
'description' => __( 'Select a default shipping option for the orders shipped internationally (outside the EU borders).', 'postnl-for-woocommerce' ),
'default' => 'parcel_non_eu|track_and_trace|insured_plus',
'for_country' => array( 'BE' ),
'options' => array(
'parcel_non_eu|track_and_trace|insured_plus' => __( 'Parcel non-EU + Track & Trace + Insured Plus', 'postnl-for-woocommerce' ),
),
);
}
$country_fields = array_filter(
$setting_fields,
function ( $field ) use ( $country, $only_field_country ) {
if ( empty( $field['for_country'] ) && false === $only_field_country ) {
return true;
}
if ( ! empty( $field['for_country'] ) && is_array( $field['for_country'] ) && in_array( $country, $field['for_country'], true ) ) {
return true;
}
if ( ! empty( $field['for_country'] ) && $field['for_country'] === $country ) {
return true;
}
return false;
}
);
return $country_fields;
}
/**
* Return NL setting fields only.
*
* @param bool $only_field_country Flag to check if it only return for the field with 'for_country' array exists.
*
* @return array
*/
public function nl_setting_fields( $only_field_country = false ) {
return $this->filter_setting_fields( 'NL', $only_field_country );
}
/**
* Return BE setting fields only.
*
* @param bool $only_field_country Flag to check if it only return for the field with 'for_country' array exists.
*
* @return array
*/
public function be_setting_fields( $only_field_country = false ) {
return $this->filter_setting_fields( 'BE', $only_field_country, true );
}
/**
* Get setting option value based on country.
*
* @param String $field Field name.
* @param String $default_value Default value if the field value is empty.
*
* @return String
*/
public function get_country_option( $field, $default_value = '' ) {
$base_country = Utils::get_base_country();
$fields_country = array_keys( $this->filter_setting_fields( $base_country, false ) );
return in_array( $field, $fields_country, true ) ? $this->get_option( $field, $default_value ) : '';
}
/**
* Get API Key from the settings.
*
* @return String
*/
public function get_api_key() {
return $this->get_country_option( 'api_keys', '' );
}
/**
* Get sandbox API Key from the settings.
*
* @return String
*/
public function get_api_key_sandbox() {
return $this->get_country_option( 'api_keys_sandbox', '' );
}
/**
* Option storing the SHA-256 hash of the new API key value that last
* passed validation. Storing a hash (rather than a global yes/no flag)
* binds the validated state to the exact key value, so any out-of-band
* edit or partial save naturally invalidates the flag.
*/
const NEW_API_KEY_VALIDATED_HASH_OPTION = 'postnl_api_keys_new_validated_hash';
/**
* Get the raw value of the new sandbox API key as entered by the merchant.
*
* @return string
*/
public function get_api_key_sandbox_new() {
return trim( (string) $this->get_country_option( 'api_keys_sandbox_new', '' ) );
}
/**
* Get the raw value of the new API key for the current environment.
*
* PostNL issues environment-scoped keys, so sandbox and production each have
* their own new-key field. Returning the field that matches the active
* environment keeps every V4 consumer (Service_Factory, the adoption header,
* the effective-key fallback) pointed at the right key without each caller
* having to branch on the environment itself.
*
* @return string
*/
public function get_api_key_new() {
if ( $this->is_sandbox() ) {
return $this->get_api_key_sandbox_new();
}
return trim( (string) $this->get_country_option( 'api_keys_new', '' ) );
}
/**
* The original (pre-migration) API key for the current environment.
*
* @return string
*/
public function get_original_api_key() {
return $this->is_sandbox()
? trim( (string) $this->get_api_key_sandbox() )
: trim( (string) $this->get_api_key() );
}
/**
* Name of the option storing the validated-key hash for a given environment.
*
* The validated flag is environment-scoped: a key validated in production must
* not be treated as validated in sandbox (and vice versa), otherwise the V4
* gate would route a production key against the sandbox host, or the reverse.
* A single option cannot hold both, since validating one environment would
* overwrite the other, so each environment gets its own option.
*
* @param bool|null $is_sandbox Environment to scope to, or null for the current one.
*
* @return string
*/
protected function validated_hash_option_name( $is_sandbox = null ) {
$is_sandbox = ( null === $is_sandbox ) ? $this->is_sandbox() : (bool) $is_sandbox;
return self::NEW_API_KEY_VALIDATED_HASH_OPTION . ( $is_sandbox ? '_sandbox' : '' );
}
/**
* Whether the currently-entered new API key matches the key that last
* passed validation against the PostNL API for the current environment.
*
* @return bool
*/
public function is_api_key_new_validated() {
return $this->is_api_key_new_validated_value( $this->get_api_key_new() );
}
/**
* Whether a specific key value matches the key that last passed
* validation. Takes the value explicitly so callers (e.g. the save-time
* handler) can check the freshly-entered key without relying on the
* settings object's in-memory cache being up to date.
*
* @param string $key Candidate key value.
* @param bool|null $is_sandbox Environment to check, or null for the current one.
*
* @return bool
*/
public function is_api_key_new_validated_value( $key, $is_sandbox = null ) {
$key = trim( (string) $key );
if ( '' === $key ) {
return false;
}
$stored = (string) get_option( $this->validated_hash_option_name( $is_sandbox ), '' );
if ( '' === $stored ) {
return false;
}
return hash_equals( $stored, hash( 'sha256', $key ) );
}
/**
* Record that the currently-entered new API key has passed validation,
* or clear the flag entirely. The hash binds the flag to the exact key
* value the merchant just successfully tested, scoped to its environment.
*
* @param bool $validated Validation outcome.
* @param string|null $key The exact key value that was validated. When
* omitted, falls back to the stored setting —
* but callers running mid-save should pass the
* freshly-entered value to avoid hashing a
* stale cached value.
* @param bool|null $is_sandbox Environment to scope to, or null for the current one.
*/
public function set_api_key_new_validated( $validated, $key = null, $is_sandbox = null ) {
$option = $this->validated_hash_option_name( $is_sandbox );
if ( ! $validated ) {
delete_option( $option );
return;
}
$key = trim( (string) ( null === $key ? $this->get_api_key_new() : $key ) );
if ( '' === $key ) {
delete_option( $option );
return;
}
update_option( $option, hash( 'sha256', $key ) );
}
/**
* Return the API key the plugin should actually send to PostNL for the
* current environment. Falls back to the original key whenever the new
* key is empty, identical, or has not been validated.
*
* @return string
*/
public function get_effective_api_key() {
$original = $this->get_original_api_key();
$new_key = $this->get_api_key_new();
if ( '' === $new_key ) {
return $original;
}
if ( $new_key === $original ) {
return $original;
}
if ( ! $this->is_api_key_new_validated() ) {
return $original;
}
return $new_key;
}
/**
* Option storing the reason the new key last failed validation, so the
* settings status row can tell "key rejected" apart from "could not reach
* PostNL" or "customer details missing" — all of which report the same
* "Entered" header value. Only consulted when the header value is "Entered".
*/
const NEW_API_KEY_STATUS_OPTION = 'postnl_api_keys_new_status';
/**
* Map the new-key state to its NewKey header value. Kept as one pure method
* so the outgoing header and the settings status row can never disagree.
*
* - "No" : the new-key field is empty.
* - "Same" : the new-key field matches the original key.
* - "Entered" : a distinct key was entered but has not passed validation.
* - "Yes" : a distinct new key has been entered and validated.
*
* "Yes" is gated on validation because PostNL defines it as a key that
* "works"; a key that failed our validation reports "Entered" so PostNL does
* not count the merchant as ready to migrate.
*
* @param string $new_key The entered new key.
* @param string $original The original (pre-migration) key.
* @param bool $validated Whether the new key passed validation.
*
* @return string
*/
protected function derive_new_key_header_value( $new_key, $original, $validated ) {
if ( '' === $new_key ) {
return 'No';
}
if ( $new_key === $original ) {
return 'Same';
}
if ( ! $validated ) {
return 'Entered';
}
return 'Yes';
}
/**
* Value for the NewKey header sent on every outgoing API call, computed for
* the current environment.
*
* @return string
*/
public function get_new_key_header_value() {
return $this->derive_new_key_header_value(
$this->get_api_key_new(),
$this->get_original_api_key(),
$this->is_api_key_new_validated()
);
}
/**
* Persist (or clear) the reason the new key last failed validation, scoped
* to the given environment.
*
* @param string $reason One of 'invalid', 'rejected', 'unreachable', 'missing', or '' to clear.
* @param bool|null $is_sandbox Environment to scope to, or null for the current one.
*/
public function set_new_key_status_reason( $reason, $is_sandbox = null ) {
$option = self::NEW_API_KEY_STATUS_OPTION . ( $this->resolve_is_sandbox( $is_sandbox ) ? '_sandbox' : '' );
if ( '' === $reason ) {
delete_option( $option );
return;
}
update_option( $option, $reason );
}
/**
* PostNL developer portal page for the API migration. It explains how to
* upgrade PHP, how to request a new API key via the Self Service (SSAM)
* module, and when to update the plug-in for API v4. Confirmed by PostNL as
* the destination for the migration banners (in place of the Self Service
* portal directly), so customers get the full step-by-step instructions first.
*/
const SELF_SERVICE_URL = 'https://developer.postnl.nl/integration-with-postnl/api-overview/future-proof-api-s/plug-ins/';
/**
* Link to the PostNL Business Portal Self Service module, reused across the
* status row and the migration banner.
*
* @return string
*/
public function self_service_link() {
return sprintf(
'%2$s',
esc_url( self::SELF_SERVICE_URL ),
esc_html__( 'Self Service module on the PostNL Business Portal', 'postnl-for-woocommerce' )
);
}
/**
* Resolve the status of the new key for a given environment: the header value
* plus a colour, label, a one-line summary and a "what to do next" paragraph
* for the settings status row. The label and colour derive from the same
* header value the API calls send, so the screen and PostNL's adoption count
* can never contradict each other.
*
* @param bool|null $is_sandbox Environment to describe, or null for the current one.
*
* @return array{header:string,label:string,color:string,summary:string,description:string}
*/
public function get_new_key_status( $is_sandbox = null ) {
$is_sandbox = $this->resolve_is_sandbox( $is_sandbox );
$new_key = $is_sandbox ? $this->get_api_key_sandbox_new() : trim( (string) $this->get_country_option( 'api_keys_new', '' ) );
$original = $is_sandbox ? trim( (string) $this->get_api_key_sandbox() ) : trim( (string) $this->get_api_key() );
$validated = $this->is_api_key_new_validated_value( $new_key, $is_sandbox );
// The rendered row describes what is actually stored, so the validated
// state it shows is a saved one.
return $this->build_new_key_status( $new_key, $original, $validated, $is_sandbox, true, $this->resolve_new_key_status_reason( $is_sandbox ) );
}
/**
* Map a new-key state to the status row's colour, label, one-line summary and
* "what to do next" paragraph. Pure: every input is passed in, so the settings
* render path and the on-blur endpoint produce identical copy for the same
* state and cannot drift. The label and colour follow the same header value
* the API calls send, so the screen and PostNL's adoption count agree.
*
* @param string $new_key The entered new key.
* @param string $original The original (pre-migration) key; '' on a fresh install.
* @param bool $validated Whether the new key has passed validation.
* @param bool $is_sandbox Environment being described.
* @param bool $saved Whether $validated reflects a saved state. A key that
* validates on blur but has not been saved yet gets the
* amber "works, not saved" state instead of green.
* @param string $reason Persisted/derived failure reason for an entered key.
*
* @return array{header:string,label:string,color:string,summary:string,description:string}
*/
public function build_new_key_status( $new_key, $original, $validated, $is_sandbox, $saved, $reason = '' ) {
unset( $is_sandbox );
$header = $this->derive_new_key_header_value( $new_key, $original, $validated );
$link = $this->self_service_link();
$has_old = '' !== trim( (string) $original );
switch ( $header ) {
case 'Yes':
if ( ! $saved ) {
return array(
'header' => $header,
'label' => __( 'Works, not saved yet', 'postnl-for-woocommerce' ),
'color' => '#dba617',
'summary' => __( 'This key works. Save changes to start using it.', 'postnl-for-woocommerce' ),
'description' => '',
);
}
return array(
'header' => $header,
'label' => __( 'Valid', 'postnl-for-woocommerce' ),
'color' => '#008a20',
'summary' => __( 'Your API key is working and will be used for the new PostNL APIs.', 'postnl-for-woocommerce' ),
'description' => '',
);
case 'Same':
return array(
'header' => $header,
'label' => __( 'Same as old key', 'postnl-for-woocommerce' ),
'color' => '#dba617',
'summary' => __( 'This is the key you already had, not a new one.', 'postnl-for-woocommerce' ),
// translators: %s is a link to the PostNL Business Portal Self Service module.
'description' => sprintf( __( 'You need a separate key. Request one from the %s, then paste it into the field above.', 'postnl-for-woocommerce' ), $link ),
);
case 'Entered':
return array_merge(
array( 'header' => $header ),
$this->get_new_key_invalid_copy( $reason, $has_old, $link )
);
default:
return array(
'header' => $header,
'label' => __( 'Not set', 'postnl-for-woocommerce' ),
'color' => '#757575',
'summary' => __( 'You have not entered your API key yet.', 'postnl-for-woocommerce' ),
'description' => $has_old
// translators: %s is a link to the PostNL Business Portal Self Service module.
? sprintf( __( 'Request your new key from the %s and enter it above to switch over from your old key.', 'postnl-for-woocommerce' ), $link )
// translators: %s is a link to the PostNL Business Portal Self Service module.
: sprintf( __( 'Request your key from the %s and enter it above — the plug-in needs it to connect to PostNL.', 'postnl-for-woocommerce' ), $link ),
);
}
}
/**
* The failure reason to describe in the status row. Missing customer details
* is decided live — if they are blank now, that is the reason whatever the
* last save recorded — otherwise the reason persisted at save time is used.
*
* @param bool $is_sandbox Environment to resolve for.
*
* @return string One of the Key_Validator REASON_* slugs, or ''.
*/
protected function resolve_new_key_status_reason( $is_sandbox ) {
if ( '' === trim( (string) $this->get_customer_code() ) || '' === trim( (string) $this->get_customer_num() ) ) {
return Key_Validator::REASON_MISSING;
}
return (string) get_option( self::NEW_API_KEY_STATUS_OPTION . ( $is_sandbox ? '_sandbox' : '' ), '' );
}
/**
* Label, colour, summary and description for an entered key that is not in
* use, per reason. Only a genuine rejection is red "Not valid"; an outage, a
* mismatch or missing details read as amber "could not check", so an outage
* never tells the merchant their key is invalid. Whether an old key is still
* carrying the plugin is passed in rather than re-read, so the copy is correct
* on a fresh install (where nothing is in use).
*
* @param string $reason One of the Key_Validator REASON_* slugs.
* @param bool $has_old Whether a usable old key is still stored.
* @param string $link Self Service link markup.
*
* @return array{label:string,color:string,summary:string,description:string}
*/
protected function get_new_key_invalid_copy( $reason, $has_old, $link ) {
$tail = $has_old
? __( ' The plug-in is still using your old key.', 'postnl-for-woocommerce' )
: __( ' The plug-in has no working key to connect with yet.', 'postnl-for-woocommerce' );
switch ( $reason ) {
case Key_Validator::REASON_REJECTED:
return array(
'label' => __( 'Could not check', 'postnl-for-woocommerce' ),
'color' => '#dba617',
'summary' => __( 'PostNL could not process the check.', 'postnl-for-woocommerce' ) . $tail,
'description' => __( 'This usually means the Customer Code or Customer Number does not match this key. Check them and save again.', 'postnl-for-woocommerce' ),
);
case Key_Validator::REASON_MISSING:
return array(
'label' => __( 'Not checked', 'postnl-for-woocommerce' ),
'color' => '#dba617',
'summary' => __( 'We have not checked this key yet.', 'postnl-for-woocommerce' ) . $tail,
'description' => __( 'Fill in your Customer Code and Customer Number, then save again.', 'postnl-for-woocommerce' ),
);
case Key_Validator::REASON_UNREACHABLE:
return array(
'label' => __( 'Could not check', 'postnl-for-woocommerce' ),
'color' => '#dba617',
'summary' => __( 'We could not reach PostNL to check this key.', 'postnl-for-woocommerce' ) . $tail,
'description' => __( 'This is usually temporary. Save again in a few minutes.', 'postnl-for-woocommerce' ),
);
case Key_Validator::REASON_INVALID:
default:
return array(
'label' => __( 'Not valid', 'postnl-for-woocommerce' ),
'color' => '#d63638',
'summary' => __( 'PostNL rejected this key.', 'postnl-for-woocommerce' ) . $tail,
// translators: %s is a link to the PostNL Business Portal Self Service module.
'description' => sprintf( __( 'Check that you copied the whole key with no extra spaces, then save again. If it keeps failing, request a new key from the %s.', 'postnl-for-woocommerce' ), $link ),
);
}
}
/**
* Normalise a nullable environment flag to the current environment.
*
* @param bool|null $is_sandbox Environment flag, or null for the current one.
*
* @return bool
*/
protected function resolve_is_sandbox( $is_sandbox ) {
return ( null === $is_sandbox ) ? $this->is_sandbox() : (bool) $is_sandbox;
}
/**
* Get customer number from the settings.
*
* @return String
*/
public function get_customer_num() {
return $this->get_country_option( 'customer_num', '' );
}
/**
* Get customer code from the settings.
*
* @return String
*/
public function get_customer_code() {
return $this->get_country_option( 'customer_code', '' );
}
/**
* Get location code from the settings.
*
* @return String
*/
public function get_location_code() {
/*
Temporarily hardcoded.
return $this->get_country_option( 'location_code', '' );
*/
return '123456';
}
/**
* Return true if sandbox mode is ticked.
*
* @return String
*/
public function get_environment_mode() {
return $this->get_country_option( 'environment_mode', '' );
}
/**
* Return true if sandbox mode is ticked.
*
* @return Bool
*/
public function is_sandbox() {
return ( 'sandbox' === $this->get_environment_mode() );
}
/**
* Get return address default from the settings.
*
* @return String
*/
public function get_return_address_default() {
return $this->get_country_option( 'return_address_default', '' );
}
/**
* Get return company name from the settings.
*
* @return String
*/
public function get_return_company_name() {
return $this->get_country_option( 'return_company', '' );
}
/**
* Get value of the return address or reply number.
*
* @return bool
*/
public function is_return_to_home_enabled() {
if ( 'yes' === $this->get_country_option( 'return_address_or_reply_no', '' ) ) {
return true;
}
return false;
}
/**
* Get value of the return address or reply number.
*
* @return bool
*/
public function is_smart_return_enabled() {
if ( 'yes' === $this->get_country_option( 'activate_smart_return', '' ) ) {
return true;
}
return false;
}
/**
* Get return reply number from the settings.
*
* @return String
*/
public function get_return_reply_number() {
return $this->get_country_option( 'return_replynumber', '' );
}
/**
* Get freepost city from the settings.
*
* @return String
*/
public function get_freepost_city() {
return $this->get_country_option( 'freepost_city', '' );
}
/**
* Get freepost zipcode from the settings.
*
* @return String
*/
public function get_freepost_zipcode() {
return $this->get_country_option( 'freepost_zip', '' );
}
/**
* Get return street address from the settings.
*
* @return String
*/
public function get_return_address_street() {
return $this->get_country_option( 'return_address_street', '' );
}
/**
* Get return house number address from the settings.
*
* @return String
*/
public function get_return_address_house_no() {
return $this->get_country_option( 'return_address_house_no', '' );
}
/**
* Get return house number extension address from the settings.
*
* @return String
*/
public function get_return_address_house_noext() {
return $this->get_country_option( 'return_address_house_noext', '' );
}
/**
* Get return city address from the settings.
*
* @return String
*/
public function get_return_city() {
return $this->get_country_option( 'return_address_city', '' );
}
/**
* Get return state address from the settings.
*
* @return String
*/
public function get_return_state() {
return $this->get_country_option( 'return_address_state', '' );
}
/**
* Get return state address from the settings.
*
* @return String
*/
public function get_return_zipcode() {
return $this->get_country_option( 'return_address_zip', '' );
}
/**
* Get return phone number from the settings.
*
* @return String
*/
public function get_return_phone() {
return $this->get_country_option( 'return_phone', '' );
}
/**
* Get return email from the settings.
*
* @return String
*/
public function get_return_email() {
return $this->get_country_option( 'return_email', '' );
}
/**
* Get return customer code from the settings.
*
* @return String
*/
public function get_return_customer_code() {
return $this->get_country_option( 'return_customer_code', '' );
}
/**
* Get return shipment and labels select value.
*
* PostNL discontinues the "Shipment & Return" label product on 1 July 2026.
* Any value still stored as 'shipping_return' is coerced to 'none' at read
* time so outbound label generation never requests the discontinued product,
* even on stores the one-time revert migration has not reached.
*
* @return String
*/
public function get_return_shipment_and_labels() {
$value = $this->get_country_option( 'return_shipment_and_labels', '' );
return 'shipping_return' === $value ? 'none' : $value;
}
/**
* Get value of the return shipment and labels all checkbox.
*
* @return String
*/
public function get_return_shipment_and_labels_all() {
return $this->get_country_option( 'return_shipment_and_labels_all', '' );
}
/**
* Get return customer code from the settings.
*
* @return String
*/
public function get_return_direct_print_label() {
return $this->get_country_option( 'return_direct_print_label', '' );
}
/**
* Return true if 'print returnlabel directly with shipping label' field is ticked.
*
* @return Bool
*/
public function is_return_direct_print_enabled() {
return ( 'yes' === $this->get_return_direct_print_label() );
}
/**
* Get the letterboxparcel (24 hours) extra fee from the settings.
*
* @since 5.9.6
*
* @return float
*/
public function get_letterbox_24_fee() {
return (float) $this->get_country_option( 'letterbox_24_fee' );
}
/**
* Get enable delivery from the settings.
*
* @return String
*/
public function get_enable_delivery() {
return $this->get_country_option( 'enable_delivery', '' );
}
/**
* Return true if delivery field is ticked.
*
* @return Bool
*/
public function is_delivery_enabled() {
return ( 'yes' === $this->get_enable_delivery() );
}
/**
* Get enable pickup points from the settings.
*
* @return String
*/
public function get_enable_pickup_points() {
return $this->get_country_option( 'enable_pickup_points' );
}
/**
* Get pick-up delivery fee from the settings.
*
* @return float
*/
public function get_pickup_delivery_fee() {
return (float) $this->get_country_option( 'pickup_delivery_fee' );
}
/**
* Return true if delivery days field is ticked.
*
* @return Bool
*/
public function is_pickup_points_enabled() {
return ( 'yes' === $this->get_enable_pickup_points() );
}
/**
* Get number pickup points from the settings.
*
* @return Int
*/
public function get_number_pickup_points() {
/*
Temporarily hardcoded.
return $this->get_country_option( 'number_pickup_points' );
*/
return 3;
}
/**
* Get enable delivery days from the settings.
*
* @return String
*/
public function get_enable_delivery_days() {
return $this->get_country_option( 'enable_delivery_days' );
}
/**
* Return true if delivery days field is ticked.
*
* @return Bool
*/
public function is_delivery_days_enabled() {
return ( 'yes' === $this->get_enable_delivery_days() );
}
/**
* Get delivery days fees the settings.
*
* @return float
*/
public function get_delivery_days_fee() {
return (float) $this->get_country_option( 'delivery_days_fee' );
}
/**
* Get letterbox fee from the settings.
* Returns null when the field is empty (meaning: do not override).
*
* @return float|null
*/
public function get_letterbox_fee() {
$value = $this->get_country_option( 'letterbox_fee' );
return ( '' !== $value ) ? (float) $value : null;
}
/**
* Get number delivery days from the settings.
*
* @return Int
*/
public function get_number_delivery_days() {
return $this->get_country_option( 'number_delivery_days' );
}
/**
* Get default checkout tab from the settings.
*
* @return string 'delivery_day' or 'dropoff_points'
*/
public function get_default_checkout_tab() {
$value = $this->get_country_option( 'default_checkout_tab' );
if ( in_array( $value, array( 'delivery_day', 'dropoff_points' ), true ) ) {
return $value;
}
if ( 'BE' === Utils::get_base_country() ) {
// TODO: gate on is_pickup_points_enabled(). When pickup is disabled
// we still return 'dropoff_points' here, an id no tab list contains.
// Container.php and the React resolver both fall back, so it's
// harmless today, but the contract should be: never return an id
// that can't render. See PR #306 review.
return 'dropoff_points';
}
return 'delivery_day';
}
/**
* Get enable evening delivery from the settings.
*
* @return String
*/
public function get_enable_evening_delivery() {
return $this->get_country_option( 'enable_evening_delivery' );
}
/**
* Return true if evening delivery field is ticked.
*
* @return Bool
*/
public function is_evening_delivery_enabled() {
return ( 'yes' === $this->get_enable_evening_delivery() );
}
/**
* Get evening delivery fee from the settings.
*
* @return String
*/
public function get_evening_delivery_fee() {
return $this->get_country_option( 'evening_delivery_fee' );
}
/**
* Get enable morning delivery from the settings.
*
* @return String
*/
public function get_enable_morning_delivery() {
return $this->get_country_option( 'enable_morning_delivery' );
}
/**
* Return true if evening delivery field is ticked.
*
* @return Bool
*/
public function is_morning_delivery_enabled() {
return ( 'yes' === $this->get_enable_morning_delivery() );
}
/**
* Get evening delivery fee from the settings.
*
* @return String
*/
public function get_morning_delivery_fee() {
return $this->get_country_option( 'morning_delivery_fee' );
}
/**
* Get transit time value from the settings.
*
* @return String
*/
public function get_transit_time() {
return $this->get_country_option( 'transit_time', '' );
}
/**
* Get cut off time value from the settings.
*
* @return String
*/
public function get_cut_off_time() {
return $this->get_country_option( 'cut_off_time', '' );
}
/**
* Get dropoff monday value from the settings.
*
* @return String
*/
public function get_dropoff_monday() {
return $this->get_country_option( 'dropoff_day_mon', '' );
}
/**
* Return true if dropoff monday field is ticked.
*
* @return Bool
*/
public function is_dropoff_monday_enabled() {
return ( 'yes' === $this->get_dropoff_monday() );
}
/**
* Get dropoff tuesday value from the settings.
*
* @return String
*/
public function get_dropoff_tuesday() {
return $this->get_country_option( 'dropoff_day_tue', '' );
}
/**
* Return true if dropoff tuesday field is ticked.
*
* @return Bool
*/
public function is_dropoff_tuesday_enabled() {
return ( 'yes' === $this->get_dropoff_tuesday() );
}
/**
* Get dropoff wednesday value from the settings.
*
* @return String
*/
public function get_dropoff_wednesday() {
return $this->get_country_option( 'dropoff_day_wed', '' );
}
/**
* Return true if dropoff wednesday field is ticked.
*
* @return Bool
*/
public function is_dropoff_wednesday_enabled() {
return ( 'yes' === $this->get_dropoff_wednesday() );
}
/**
* Get dropoff thursday value from the settings.
*
* @return String
*/
public function get_dropoff_thursday() {
return $this->get_country_option( 'dropoff_day_thu', '' );
}
/**
* Return true if dropoff thursday field is ticked.
*
* @return Bool
*/
public function is_dropoff_thursday_enabled() {
return ( 'yes' === $this->get_dropoff_thursday() );
}
/**
* Get dropoff friday value from the settings.
*
* @return String
*/
public function get_dropoff_friday() {
return $this->get_country_option( 'dropoff_day_fri', '' );
}
/**
* Return true if dropoff friday field is ticked.
*
* @return Bool
*/
public function is_dropoff_friday_enabled() {
return ( 'yes' === $this->get_dropoff_friday() );
}
/**
* Get dropoff saturday value from the settings.
*
* @return String
*/
public function get_dropoff_saturday() {
return $this->get_country_option( 'dropoff_day_sat', '' );
}
/**
* Return true if dropoff saturday field is ticked.
*
* @return Bool
*/
public function is_dropoff_saturday_enabled() {
return ( 'yes' === $this->get_dropoff_saturday() );
}
/**
* Get dropoff sunday value from the settings.
*
* @return String
*/
public function get_dropoff_sunday() {
return $this->get_country_option( 'dropoff_day_sun', '' );
}
/**
* Return true if dropoff sunday field is ticked.
*
* @return Bool
*/
public function is_dropoff_sunday_enabled() {
return ( 'yes' === $this->get_dropoff_sunday() );
}
/**
* Get dropoff days from the settings.
*
* @return array
*/
public function get_dropoff_days() {
$dropoff_days = array();
if ( $this->is_dropoff_monday_enabled() ) {
$dropoff_days[] = 'mon';
}
if ( $this->is_dropoff_tuesday_enabled() ) {
$dropoff_days[] = 'tue';
}
if ( $this->is_dropoff_wednesday_enabled() ) {
$dropoff_days[] = 'wed';
}
if ( $this->is_dropoff_thursday_enabled() ) {
$dropoff_days[] = 'thu';
}
if ( $this->is_dropoff_friday_enabled() ) {
$dropoff_days[] = 'fri';
}
if ( $this->is_dropoff_saturday_enabled() ) {
$dropoff_days[] = 'sat';
}
if ( $this->is_dropoff_sunday_enabled() ) {
$dropoff_days[] = 'sun';
}
return $dropoff_days;
}
/**
* Get excluded dropoff days from the settings.
*
* @return array
*/
public function get_excluded_dropoff_days() {
$completed_days = array_keys( Utils::days_of_week() );
$dropoff_days = $this->get_dropoff_days();
return array_diff( $completed_days, $dropoff_days );
}
/**
* Get globalpack type barcode from the settings.
*
* @return String
*/
public function get_globalpack_barcode_type() {
return $this->get_country_option( 'globalpack_barcode_type', '' );
}
/**
* Get globalpack customer code from the settings.
*
* @return String
*/
public function get_globalpack_customer_code() {
return $this->get_country_option( 'globalpack_customer_code', '' );
}
/**
* Get HS Tariff code from the settings.
*
* @return String
*/
public function get_hs_tariff_code() {
return $this->get_country_option( 'hs_tariff_code', '' );
}
/**
* Get HS Tariff code from the settings.
*
* @return String
*/
public function get_country_origin() {
return $this->get_country_option( 'country_origin', '' );
}
/**
* Get label format from the settings.
*
* @return String
*/
public function get_label_format() {
return $this->get_country_option( 'label_format', '' );
}
/**
* Get printer type from the settings.
* PDF by default.
*
* @return String
*/
public function get_printer_type() {
$printer_type = $this->get_country_option( 'printer_type', '' );
$resolution = (int) $this->get_country_option( 'printer_type_resolution', 600 );
switch ( $printer_type ) {
case 'JPG':
return sprintf( 'GraphicFile|JPG %d dpi', $resolution );
case 'GIF':
return sprintf( 'GraphicFile|GIF %d dpi', $resolution );
case 'ZPL':
return sprintf( 'Zebra|Generic ZPL II %d dpi', $resolution );
default:
return 'GraphicFile|PDF';
}
}
/**
* Get shipping confirmation email text value from the settings.
*
* @return String
*/
public function get_shipping_confirmation_email() {
return $this->get_country_option( 'shipping_confirmation_email', '' );
}
/**
* Get woocommerce email checkbox value from the settings.
*
* @return String
*/
public function get_woocommerce_email() {
return $this->get_country_option( 'woocommerce_email', '' );
}
/**
* Return true if woocommerce email field is ticked.
*
* @return Bool
*/
public function is_woocommerce_email_enabled() {
return ( 'yes' === $this->get_woocommerce_email() );
}
/**
* Get woocommerce email text value from the settings.
*
* @return String
*/
public function get_woocommerce_email_text() {
return $this->get_country_option( 'woocommerce_email_text', '' );
}
/**
* Get check Netherlands address value from the settings.
*
* @return String
*/
public function get_validate_nl_address() {
return $this->get_country_option( 'validate_nl_address', '' );
}
/**
* Return true if check Netherlands address field is ticked.
*
* @return Bool
*/
public function is_validate_nl_address_enabled() {
return ( 'yes' === $this->get_validate_nl_address() );
}
/**
* Get reorder Netherlands address value from the settings.
*
* @return String
*/
public function get_reorder_nl_address() {
return $this->get_country_option( 'reorder_nl_address', '' );
}
/**
* Return true if reorder Netherlands address field is ticked.
*
* @return Bool
*/
public function is_reorder_nl_address_enabled() {
return ( 'yes' === $this->get_reorder_nl_address() );
}
/**
* Get enable logging value from the settings.
*
* @return String
*/
public function get_enable_logging() {
return $this->get_country_option( 'enable_logging', '' );
}
/**
* Return true if enable logging field is ticked.
*
* @return Bool
*/
public function is_logging_enabled() {
return ( 'yes' === $this->get_enable_logging() );
}
/**
* Get all shipping options.
*
* @param string $zone Shipping zone, available options: 'ne' - to Netherlands, 'be' - to Belgium, 'eu' - to European Union, 'row' - international shipping.
*
* @return array
*/
public function get_default_shipping_options( $zone ) {
$shipping_options = $this->get_country_option( 'default_shipping_options_' . strtolower( $zone ), '' );
return Utils::prepare_shipping_options( $shipping_options );
}
/**
* Get default automatic letterboxparcel product from the settings.
*
* @since 5.9.6
*
* @return String
*/
public function get_default_automatic_letterboxparcel_product() {
return $this->get_country_option( 'default_automatic_letterboxparcel_product', 'letterbox' );
}
/**
* Return array of shipping methods.
*
* @return array.
*/
public function get_shipping_methods() {
return wp_list_pluck( WC()->shipping()->shipping_methods, 'method_title', 'id' );
}
/**
* Get supported shipping methods from the settings.
*
* @return array.
*/
public function get_supported_shipping_methods() {
$suppoted_shipping_methods = (array) $this->get_option( 'supported_shipping_methods' );
// Add PostNL method by default
$suppoted_shipping_methods[] = POSTNL_SETTINGS_ID;
return $suppoted_shipping_methods;
}
/**
* Get Automatically change order status to Completed value from the settings.
*
* @return String
*/
public function get_auto_complete_order() {
return $this->get_country_option( 'auto_complete_order', '' );
}
/**
* Return true if Automatically change order status to Completed is ticked.
*
* @return Bool
*/
public function is_auto_complete_order_enabled() {
return ( 'yes' === $this->get_auto_complete_order() );
}
}