Plugin Directory

Changeset 3749063


Ignore:
Timestamp:
10/15/2026 05:50:24 PM (2 days ago)
Author:
moralogistics
Message:

Update to version 0.7.79 from GitHub

Location:
mora-delivery-for-woocommerce
Files:
2 deleted
34 edited
1 copied

Legend:

Unmodified
Added
Removed
  • mora-delivery-for-woocommerce/tags/0.7.79/includes/class-mora-admin-settings.php

    r3746963 r3749063  
    400400                }
    401401                self::$resolving_site_wide_credentials = true;
    402                 $scanned = self::scan_zone_instances_for_credentials();
     402                $scanned                               = self::scan_zone_instances_for_credentials();
    403403                self::$resolving_site_wide_credentials = false;
    404404                if ( '' === $scanned['api_key'] ) {
  • mora-delivery-for-woocommerce/tags/0.7.79/includes/class-mora-blocks-payment-method.php

    r3746963 r3749063  
    6969        public function initialize() {
    7070                $this->settings = get_option( 'woocommerce_' . $this->name . '_settings', array() );
    71                 $gateways      = WC()->payment_gateways() ? WC()->payment_gateways()->payment_gateways() : array();
    72                 $this->gateway = isset( $gateways[ $this->name ] ) ? $gateways[ $this->name ] : null;
     71                $gateways       = WC()->payment_gateways() ? WC()->payment_gateways()->payment_gateways() : array();
     72                $this->gateway  = isset( $gateways[ $this->name ] ) ? $gateways[ $this->name ] : null;
    7373        }
    7474
    … …  
    9090        public function get_payment_method_data() {
    9191                return array(
    92                         'title'          => $this->gateway ? $this->gateway->title : '',
    93                         'description'    => $this->gateway ? $this->gateway->description : '',
     92                        'title'           => $this->gateway ? $this->gateway->title : '',
     93                        'description'     => $this->gateway ? $this->gateway->description : '',
    9494                        // Static per page load (an admin setting, not cart state), safe
    9595                        // for canMakePayment() to branch on directly; the live destination
  • mora-delivery-for-woocommerce/tags/0.7.79/includes/class-mora-checkout-experiment.php

    r3746963 r3749063  
    176176                }
    177177
    178                 $due_today = (float) WC()->cart->get_shipping_total() + (float) WC()->cart->get_shipping_tax();
     178                $due_today       = (float) WC()->cart->get_shipping_total() + (float) WC()->cart->get_shipping_tax();
    179179                $due_on_delivery = (float) WC()->cart->get_total( 'edit' ) - $due_today;
    180180                if ( $due_on_delivery < 0 ) {
  • mora-delivery-for-woocommerce/tags/0.7.79/includes/class-mora-checkout.php

    r3746963 r3749063  
    3535class Mora_Checkout {
    3636
    37         const NONCE_ACTION = 'mora_wc_set_coordinates';
     37        const NONCE_ACTION            = 'mora_wc_set_coordinates';
    3838        const COORDS_TRANSIENT_PREFIX = 'mora_coords_';
    3939
    4040        // See get_places_api_key()'s own docblock for why this is cached at all,
    4141        // and why success and failure carry different TTLs.
    42         const PLACES_KEY_TRANSIENT = 'mora_wc_places_key_cache';
    43         const PLACES_KEY_CACHE_TTL = 12 * HOUR_IN_SECONDS;
     42        const PLACES_KEY_TRANSIENT         = 'mora_wc_places_key_cache';
     43        const PLACES_KEY_CACHE_TTL         = 12 * HOUR_IN_SECONDS;
    4444        const PLACES_KEY_FAILURE_CACHE_TTL = 5 * MINUTE_IN_SECONDS;
    4545
    … …  
    127127         */
    128128        public static function locator_script_data() {
     129                $ng_states = WC()->countries->get_states( 'NG' );
    129130                return array(
    130131                        'placesApiKey' => self::get_places_api_key(),
    … …  
    136137                        // code => name list classic's own <select> options already reflect,
    137138                        // sourced from the same place WooCommerce itself does.
    138                         'ngStates'     => WC()->countries->get_states( 'NG' ) ?: array(),
     139                        'ngStates'     => $ng_states ? $ng_states : array(),
    139140                        'i18n'         => array(
    140141                                // The widget is a locator (sets coordinates, city, state, LGA for
    … …  
    145146                                // phase adds a real, required field literally called "Landmark", so the
    146147                                // widget can no longer describe itself the same way.
    147                                 'locatorLabel' => __( 'Find your delivery address', 'mora-delivery-for-woocommerce' ),
     148                                'locatorLabel'       => __( 'Find your delivery address', 'mora-delivery-for-woocommerce' ),
    148149                                // Measured on 20 real Lagos addresses: 18/20 resolved to door level when
    149150                                // the query contained a house number. That instruction is the single most
    … …  
    153154                                // (New) release is not confirmed against Google's primary docs from this
    154155                                // environment).
    155                                 'locatorHint'  => __( 'Start with your house number, like 12 Allen Avenue. We use this to work out your delivery fee.', 'mora-delivery-for-woocommerce' ),
     156                                'locatorHint'        => __( 'Start with your house number, like 12 Allen Avenue. We use this to work out your delivery fee.', 'mora-delivery-for-woocommerce' ),
    156157                                // A generic example address, deliberately not the Mora hub's own street
    157158                                // address (16 Adebiyi Street): that's a real, specific location tied to the
    … …  
    295296                // two days for a guest. The pin has no reason to outlive the session it belongs to.
    296297                $ttl = is_user_logged_in() ? WEEK_IN_SECONDS : 2 * DAY_IN_SECONDS;
    297                 set_transient( $key, array(
    298                         'lat'    => $lat,
    299                         'lon'    => $lon,
    300                         'lga'    => $lga,
    301                         'pinned' => $pinned,
    302                 ), $ttl );
     298                set_transient(
     299                        $key,
     300                        array(
     301                                'lat'    => $lat,
     302                                'lon'    => $lon,
     303                                'lga'    => $lga,
     304                                'pinned' => $pinned,
     305                        ),
     306                        $ttl
     307                );
    303308        }
    304309
  • mora-delivery-for-woocommerce/tags/0.7.79/includes/class-mora-fields.php

    r3746963 r3749063  
    9999                                'classic_type'      => 'text',
    100100                                'required_for_mora' => false, // always optional, Mora rate or not; see the class
    101                                                                // docblock for why this was changed.
     101                                                                                                // docblock for why this was changed.
    102102                                'label'             => __( 'Landmark', 'mora-delivery-for-woocommerce' ),
    103103                                'placeholder'       => __( 'A shop, school or building we can find', 'mora-delivery-for-woocommerce' ),
    … …  
    189189
    190190        /**
     191         * WooCommerce's built-in pickup method ids: `local_pickup` on classic
     192         * checkout, `pickup_location` on Blocks checkout (WooCommerce's newer
     193         * Local Pickup). Both carry Mora's hub-pickup fee.
     194         */
     195        const PICKUP_METHOD_IDS = array( 'local_pickup', 'pickup_location' );
     196
     197        /**
     198         * Whether a shipping method id is one of WooCommerce's pickup methods.
     199         *
     200         * @param string $method_id Shipping method id.
     201         * @return bool
     202         */
     203        public static function is_pickup_method( $method_id ) {
     204                return in_array( $method_id, self::PICKUP_METHOD_IDS, true );
     205        }
     206
     207        /**
    191208         * has_mora_shipping_line()'s sibling for hub pickup: does this order use
    192          * WooCommerce's own built-in Local Pickup method (method id
    193          * `local_pickup`, not a Mora-owned one). Mora_Order_Push::can_push()
     209         * one of WooCommerce's own built-in pickup methods (PICKUP_METHOD_IDS,
     210         * not a Mora-owned one). Mora_Order_Push::can_push()
    194211         * reads this to also push a pickup order, alongside a Mora delivery
    195212         * one: the customer collects at the Mora hub instead of a rider
    … …  
    199216        public static function has_local_pickup_line( $order ) {
    200217                foreach ( $order->get_items( 'shipping' ) as $item ) {
    201                         if ( 'local_pickup' === $item->get_method_id() ) {
     218                        if ( self::is_pickup_method( $item->get_method_id() ) ) {
    202219                                return true;
    203220                        }
    … …  
    252269         * Landmark is never enforced here, on any rate; see the class docblock for why.
    253270         */
    254         public static function validate_blocks_phone_and_landmark( $context, $result ) {
     271        public static function validate_blocks_phone_and_landmark( $context, $result ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed -- $result is part of the hook's signature.
    255272                $chosen_methods = WC()->session ? WC()->session->get( 'chosen_shipping_methods', array() ) : array();
    256273                if ( ! self::is_mora_rate_chosen( $chosen_methods ) ) {
  • mora-delivery-for-woocommerce/tags/0.7.79/includes/class-mora-leads-admin.php

    r3746963 r3749063  
    1717
    1818        const EXPORT_ACTION = 'mora_wc_leads_export';
     19
     20        // Shown in a list column with no value.
     21        const EMPTY_CELL = '-';
    1922
    2023        /**
    … …  
    8083        }
    8184
    82         public static function define_columns( $columns ) {
     85        public static function define_columns( $columns ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- filter signature; the default columns are replaced, not extended.
    8386                return array(
    8487                        'cb'                 => '<input type="checkbox" />',
    … …  
    9598
    9699        /**
     100         * A column value, or the empty-cell placeholder when it is empty.
     101         *
     102         * @param mixed $value Stored meta value.
     103         * @return mixed
     104         */
     105        private static function cell( $value ) {
     106                return $value ? $value : self::EMPTY_CELL;
     107        }
     108
     109        /**
    97110         * Every column but Cart is plain text, always esc_html()'d. The Cart
    98111         * column is the one exception: wc_price() returns real markup (a
    … …  
    107120                        case 'mora_lead_name':
    108121                                $name = trim( get_post_meta( $post_id, Mora_Leads::META_FIRST_NAME, true ) . ' ' . get_post_meta( $post_id, Mora_Leads::META_LAST_NAME, true ) );
    109                                 echo esc_html( $name ?: '—' );
     122                                echo esc_html( self::cell( $name ) );
    110123                                break;
    111124                        case 'mora_lead_phone':
    112                                 echo esc_html( get_post_meta( $post_id, Mora_Leads::META_PHONE, true ) ?: '—' );
     125                                echo esc_html( self::cell( get_post_meta( $post_id, Mora_Leads::META_PHONE, true ) ) );
    113126                                break;
    114127                        case 'mora_lead_email':
    115                                 echo esc_html( get_post_meta( $post_id, Mora_Leads::META_EMAIL, true ) ?: '—' );
     128                                echo esc_html( self::cell( get_post_meta( $post_id, Mora_Leads::META_EMAIL, true ) ) );
    116129                                break;
    117130                        case 'mora_lead_city':
    118                                 echo esc_html( get_post_meta( $post_id, Mora_Leads::META_CITY, true ) ?: '—' );
     131                                echo esc_html( self::cell( get_post_meta( $post_id, Mora_Leads::META_CITY, true ) ) );
    119132                                break;
    120133                        case 'mora_lead_cart':
    … …  
    124137                                break;
    125138                        case 'mora_lead_products':
    126                                 echo esc_html( get_post_meta( $post_id, Mora_Leads::META_PRODUCTS, true ) ?: '—' );
     139                                echo esc_html( self::cell( get_post_meta( $post_id, Mora_Leads::META_PRODUCTS, true ) ) );
    127140                                break;
    128141                        case 'mora_lead_seen':
    129142                                $last_seen = (int) get_post_meta( $post_id, Mora_Leads::META_LAST_SEEN, true );
    130                                 echo esc_html( $last_seen ? date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $last_seen ) : '—' );
     143                                echo esc_html( $last_seen ? date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $last_seen ) : self::EMPTY_CELL );
    131144                                break;
    132145                        case 'mora_lead_status':
    133                                 $status  = get_post_meta( $post_id, Mora_Leads::META_STATUS, true );
     146                                $status   = get_post_meta( $post_id, Mora_Leads::META_STATUS, true );
    134147                                $order_id = (int) get_post_meta( $post_id, Mora_Leads::META_ORDER_ID, true );
    135148                                if ( Mora_Leads::STATUS_CONVERTED === $status && $order_id && function_exists( 'wc_get_order' ) && wc_get_order( $order_id ) ) {
    … …  
    148161         * (a bare WooCommerce → Leads click, never a query string).
    149162         */
    150         public static function status_filter_views( $views ) {
     163        public static function status_filter_views( $views ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- filter signature; the default views are replaced, not extended.
    151164                $current = isset( $_GET['lead_status'] ) ? sanitize_key( wp_unslash( $_GET['lead_status'] ) ) : 'open'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
    152165
    … …  
    159172                $new_views = array();
    160173                foreach ( $tabs as $key => $label ) {
    161                         $count = self::count_leads_for_status( $key );
    162                         $url   = add_query_arg(
     174                        $count             = self::count_leads_for_status( $key );
     175                        $url               = add_query_arg(
    163176                                array(
    164177                                        'post_type'   => Mora_Leads::POST_TYPE,
    … …  
    167180                                admin_url( 'edit.php' )
    168181                        );
    169                         $class = $current === $key ? ' class="current"' : '';
     182                        $class             = $current === $key ? ' class="current"' : '';
    170183                        $new_views[ $key ] = sprintf(
    171184                                '<a href="%s"%s>%s <span class="count">(%d)</span></a>',
    … …  
    347360                        // computed once, at capture time, and is what conversion matching
    348361                        // also reads, so export and matching can never quietly disagree.
    349                         $phone      = get_post_meta( $lead_id, Mora_Leads::META_PHONE_NORMALIZED, true );
     362                        $phone = get_post_meta( $lead_id, Mora_Leads::META_PHONE_NORMALIZED, true );
    350363                        fputcsv(
    351364                                $out,
  • mora-delivery-for-woocommerce/tags/0.7.79/includes/class-mora-leads.php

    r3746963 r3749063  
    4545        const POST_TYPE = 'mora_lead';
    4646
    47         const META_SESSION_ID  = '_mora_lead_session_id';
    48         const META_FIRST_NAME  = '_mora_lead_first_name';
    49         const META_LAST_NAME   = '_mora_lead_last_name';
     47        const META_SESSION_ID = '_mora_lead_session_id';
     48        const META_FIRST_NAME = '_mora_lead_first_name';
     49        const META_LAST_NAME  = '_mora_lead_last_name';
    5050        // As the customer typed it (local format, e.g. 08012345678): what the
    5151        // merchant searches by and sees in the admin list. META_PHONE_NORMALIZED
    … …  
    5555        const META_PHONE            = '_mora_lead_phone';
    5656        const META_PHONE_NORMALIZED = '_mora_lead_phone_normalized';
    57         const META_EMAIL       = '_mora_lead_email';
    58         const META_CITY        = '_mora_lead_city';
    59         const META_STATE       = '_mora_lead_state';
    60         const META_CART_COUNT  = '_mora_lead_cart_count';
    61         const META_CART_TOTAL  = '_mora_lead_cart_total';
     57        const META_EMAIL            = '_mora_lead_email';
     58        const META_CITY             = '_mora_lead_city';
     59        const META_STATE            = '_mora_lead_state';
     60        const META_CART_COUNT       = '_mora_lead_cart_count';
     61        const META_CART_TOTAL       = '_mora_lead_cart_total';
    6262        // "Name (SKU), Name (SKU)..." for the whole cart at capture time, not
    6363        // one line item: a lead can span multiple products, the same reason
    6464        // cart_count/cart_total summarize the whole cart rather than one item.
    65         const META_PRODUCTS    = '_mora_lead_products';
    66         const META_FIRST_SEEN  = '_mora_lead_first_seen';
    67         const META_LAST_SEEN   = '_mora_lead_last_seen';
    68         const META_STATUS      = '_mora_lead_status';
    69         const META_ORDER_ID    = '_mora_lead_order_id';
     65        const META_PRODUCTS   = '_mora_lead_products';
     66        const META_FIRST_SEEN = '_mora_lead_first_seen';
     67        const META_LAST_SEEN  = '_mora_lead_last_seen';
     68        const META_STATUS     = '_mora_lead_status';
     69        const META_ORDER_ID   = '_mora_lead_order_id';
    7070
    7171        const STATUS_OPEN      = 'open';
    7272        const STATUS_CONVERTED = 'converted';
    7373
    74         const THROTTLE_SECONDS      = 60;
     74        const THROTTLE_SECONDS       = 60;
    7575        const RETENTION_DAYS_DEFAULT = 90;
    76         const PURGE_CRON_HOOK       = 'mora_wc_purge_leads';
     76        const PURGE_CRON_HOOK        = 'mora_wc_purge_leads';
    7777        // A daily cron deleting an unbounded set in one request will eventually
    7878        // time out; a capped, oldest-first batch clears a real backlog over a
    7979        // few days instead of failing outright on the day it's biggest.
    80         const PURGE_BATCH_SIZE      = 200;
     80        const PURGE_BATCH_SIZE = 200;
    8181
    8282        // Only these three WooCommerce order statuses mean "this order is real
    … …  
    177177         * pollutes the leads list with the merchant's own sessions.
    178178         */
    179         public static function capture_from_store_api( $customer, $request ) {
     179        public static function capture_from_store_api( $customer, $request ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed -- $request is part of the hook's signature.
    180180                if ( ! $customer instanceof WC_Customer ) {
    181181                        return;
    … …  
    243243                        // Shipping-preferred-falls-back-to-billing, the same idiom
    244244                        // Mora_Order_Push::build_payload() already uses for city/state.
    245                         'city'       => $customer->get_shipping_city() ?: $customer->get_billing_city(),
    246                         'state'      => $customer->get_shipping_state() ?: $customer->get_billing_state(),
     245                        'city'       => $customer->get_shipping_city() ? $customer->get_shipping_city() : $customer->get_billing_city(),
     246                        'state'      => $customer->get_shipping_state() ? $customer->get_shipping_state() : $customer->get_billing_state(),
    247247                        'cart_count' => WC()->cart ? WC()->cart->get_cart_contents_count() : 0,
    248248                        // 'edit' context: the raw numeric total, not a pre-formatted
    … …  
    272272                                continue;
    273273                        }
    274                         $sku    = $product->get_sku();
     274                        $sku     = $product->get_sku();
    275275                        $names[] = $sku ? $product->get_name() . ' (' . $sku . ')' : $product->get_name();
    276276                }
    … …  
    422422        }
    423423
    424         public static function reopen_lead_for_order( $order_id, $order ) {
     424        public static function reopen_lead_for_order( $order_id, $order ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed -- $order is part of the hook's signature.
    425425                $lead_ids = get_posts(
    426426                        array(
    … …  
    582582         * eraser keeps calling this until a page genuinely comes back empty.
    583583         */
    584         public static function privacy_eraser( $email_address, $page = 1 ) {
     584        public static function privacy_eraser( $email_address, $page = 1 ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed -- eraser callback signature; $page deliberately unused, see above.
    585585                $per_page = 50;
    586586                $lead_ids = get_posts(
  • mora-delivery-for-woocommerce/tags/0.7.79/includes/class-mora-locator-block.php

    r3746963 r3749063  
    5656        // Naming mirrors Mora_Payment_Gateway::COD_DISABLE_ACTION, the existing
    5757        // one-click-admin-notice pattern this feature follows.
    58         const ADD_ACTION               = 'mora_wc_add_locator_block';
    59         const RESULT_TRANSIENT_PREFIX  = 'mora_wc_locator_result_';
     58        const ADD_ACTION              = 'mora_wc_add_locator_block';
     59        const RESULT_TRANSIENT_PREFIX = 'mora_wc_locator_result_';
    6060
    6161        public static function init() {
  • mora-delivery-for-woocommerce/tags/0.7.79/includes/class-mora-order-push.php

    r3748164 r3749063  
    120120                        return true; // hub pickup: no destination to validate
    121121                }
    122                 $country = $order->get_shipping_country() ?: $order->get_billing_country();
     122                $country = self::first_filled( $order->get_shipping_country(), $order->get_billing_country() );
    123123                if ( 'NG' !== $country ) {
    124124                        $reason = sprintf(
    … …  
    763763                // A Mora delivery line always wins if present (matches can_push()'s own
    764764                // priority): only treat this as a hub-pickup order when there is no
    765                 // Mora line at all. WooCommerce's built-in Local Pickup method
    766                 // (`local_pickup`) is what carries the pickup fee in that case; the
     765                // Mora line at all. WooCommerce's built-in pickup method
     766                // (Mora_Fields::PICKUP_METHOD_IDS) carries the pickup fee in that case; the
    767767                // customer collects at the Mora hub, so there is no delivery address
    768768                // to send at all.
    769                 $is_pickup      = empty( $mora_lines );
    770                 $expected_method = $is_pickup ? 'local_pickup' : 'mora';
    771                 $fee_lines      = $is_pickup
     769                $is_pickup   = empty( $mora_lines );
     770                $fee_lines   = $is_pickup
    772771                        ? array_values(
    773772                                array_filter(
    774773                                        $shipping_lines,
    775774                                        function ( $item ) {
    776                                                 return 'local_pickup' === $item->get_method_id();
     775                                                return Mora_Fields::is_pickup_method( $item->get_method_id() );
    777776                                        }
    778777                                )
    779778                        )
    780779                        : $mora_lines;
    781                 $other_lines    = array_values(
     780                $other_lines = array_values(
    782781                        array_filter(
    783782                                $shipping_lines,
    784                                 function ( $item ) use ( $expected_method ) {
    785                                         return $expected_method !== $item->get_method_id();
     783                                function ( $item ) use ( $is_pickup ) {
     784                                        $id = $item->get_method_id();
     785                                        return $is_pickup ? ! Mora_Fields::is_pickup_method( $id ) : 'mora' !== $id;
    786786                                }
    787787                        )
    … …  
    801801                }
    802802
    803                 $delivery_fee  = 0.0;
    804                 $shipping_tax  = 0.0;
     803                $delivery_fee = 0.0;
     804                $shipping_tax = 0.0;
    805805                foreach ( $fee_lines as $line ) {
    806806                        $delivery_fee += (float) $line->get_total();
    … …  
    832832                // Mora delivery rate (Mora_Payment_Gateway::every_package_rate_or_
    833833                // false()), never on Local Pickup.
    834                 $cod_amount  = $is_mora_cod ? ( $order_total - $delivery_fee ) : 0.0;
    835                 $state_code  = $is_pickup ? null : ( $order->get_shipping_state() ?: $order->get_billing_state() );
     834                $cod_amount = $is_mora_cod ? ( $order_total - $delivery_fee ) : 0.0;
     835                $state_code = $is_pickup ? null : self::first_filled( $order->get_shipping_state(), $order->get_billing_state() );
    836836
    837837                return array(
    838838                        'merchant_order_id'  => (string) $order->get_id(),
    839839                        'site_url'           => home_url( '/' ),
    840                         'quote_id'           => $order->get_meta( '_mora_quote_id' ) ?: null,
     840                        'quote_id'           => self::filled_or_null( $order->get_meta( '_mora_quote_id' ) ),
    841841                        // Never null: OrderIn.placed_at is required and orders.placed_at is
    842842                        // NOT NULL, so a null here is a guaranteed 422, and a 422 is never
    … …  
    858858                        'fulfillment_type'   => $is_pickup ? 'pickup' : 'delivery',
    859859                        // Not sent: delivery_fee_collected, state_name. Both derived in mora-api.
    860                         'customer_name'      => trim( $order->get_shipping_first_name() . ' ' . $order->get_shipping_last_name() )
    861                                 ?: trim( $order->get_billing_first_name() . ' ' . $order->get_billing_last_name() ),
     860                        'customer_name'      => self::first_filled(
     861                                trim( $order->get_shipping_first_name() . ' ' . $order->get_shipping_last_name() ),
     862                                trim( $order->get_billing_first_name() . ' ' . $order->get_billing_last_name() )
     863                        ),
    862864                        'customer_phone'     => $order->get_billing_phone(),
    863                         'customer_phone_alt' => $order->get_meta( '_mora_phone_alt' ) ?: null,
     865                        'customer_phone_alt' => self::filled_or_null( $order->get_meta( '_mora_phone_alt' ) ),
    864866                        'customer_email'     => $order->get_billing_email(),
    865867                        // Every address/coordinate field below is null for a pickup order:
    … …  
    867869                        // delivery address to send at all (mora-api's own OrderIn requires
    868870                        // these only when fulfillment_type is "delivery").
    869                         'address_1'          => $is_pickup ? null : ( $order->get_shipping_address_1() ?: $order->get_billing_address_1() ),
    870                         'address_2'          => $is_pickup ? null : ( $order->get_shipping_address_2() ?: $order->get_billing_address_2() ),
    871                         'landmark'           => $is_pickup ? null : ( $order->get_meta( '_mora_landmark' ) ?: null ),
    872                         'city'               => $is_pickup ? null : ( $order->get_shipping_city() ?: $order->get_billing_city() ),
     871                        'address_1'          => $is_pickup ? null : self::first_filled( $order->get_shipping_address_1(), $order->get_billing_address_1() ),
     872                        'address_2'          => $is_pickup ? null : self::first_filled( $order->get_shipping_address_2(), $order->get_billing_address_2() ),
     873                        'landmark'           => $is_pickup ? null : self::filled_or_null( $order->get_meta( '_mora_landmark' ) ),
     874                        'city'               => $is_pickup ? null : self::first_filled( $order->get_shipping_city(), $order->get_billing_city() ),
    873875                        'state_code'         => $state_code,
    874                         'lga'                => $is_pickup ? null : ( $order->get_meta( '_mora_lga' ) ?: null ),
    875                         'pinned_address'     => $is_pickup ? null : ( $order->get_meta( '_mora_pinned_address' ) ?: null ),
     876                        'lga'                => $is_pickup ? null : self::filled_or_null( $order->get_meta( '_mora_lga' ) ),
     877                        'pinned_address'     => $is_pickup ? null : self::filled_or_null( $order->get_meta( '_mora_pinned_address' ) ),
    876878                        'lat'                => ( ! $is_pickup && '' !== $order->get_meta( '_mora_lat' ) ) ? (float) $order->get_meta( '_mora_lat' ) : null,
    877879                        'lon'                => ( ! $is_pickup && '' !== $order->get_meta( '_mora_lon' ) ) ? (float) $order->get_meta( '_mora_lon' ) : null,
    … …  
    887889                                array_values( $order->get_items() )
    888890                        ),
    889                         'notes'              => $order->get_customer_note() ?: null,
    890                 );
     891                        'notes'              => self::filled_or_null( $order->get_customer_note() ),
     892                );
     893        }
     894
     895        /**
     896         * The preferred value when it is truthy, otherwise the fallback. Same
     897         * result as PHP's short ternary ( $preferred ?: $fallback ), which the
     898         * coding standard disallows; both arguments are side-effect-free getters.
     899         *
     900         * @param mixed $preferred Value to use when truthy (e.g. a shipping field).
     901         * @param mixed $fallback  Value to use otherwise (e.g. the billing field).
     902         * @return mixed
     903         */
     904        private static function first_filled( $preferred, $fallback ) {
     905                return $preferred ? $preferred : $fallback;
     906        }
     907
     908        /**
     909         * The value when it is truthy, otherwise null (an empty meta string is
     910         * sent to mora-api as null, never as "").
     911         *
     912         * @param mixed $value Value to check.
     913         * @return mixed|null
     914         */
     915        private static function filled_or_null( $value ) {
     916                return $value ? $value : null;
    891917        }
    892918
  • mora-delivery-for-woocommerce/tags/0.7.79/includes/class-mora-payment-gateway.php

    r3746963 r3749063  
    9494        public function __construct() {
    9595                $this->id                 = 'mora_pay_on_delivery';
    96                 $this->icon                = '';
    97                 $this->has_fields          = false;
    98                 $this->method_title        = __( 'Mora Goods on Delivery', 'mora-delivery-for-woocommerce' );
    99                 $this->method_description  = __( 'Charges only the order\'s delivery/shipping fee at checkout through the merchant\'s own Paystack account; any goods balance is collected in cash on delivery. Available for any order regardless of shipping method, as long as this gateway is enabled and a delivery fee is present; optionally restricted to Lagos-only destinations below.', 'mora-delivery-for-woocommerce' );
     96                $this->icon               = '';
     97                $this->has_fields         = false;
     98                $this->method_title       = __( 'Mora Goods on Delivery', 'mora-delivery-for-woocommerce' );
     99                $this->method_description = __( 'Charges only the order\'s delivery/shipping fee at checkout through the merchant\'s own Paystack account; any goods balance is collected in cash on delivery. Available for any order regardless of shipping method, as long as this gateway is enabled and a delivery fee is present; optionally restricted to Lagos-only destinations below.', 'mora-delivery-for-woocommerce' );
    100100                // Deliberately no 'refunds': a Paystack refund here is a manual action in
    101101                // the merchant's own PSP dashboard (README), not something this plugin
    … …  
    108108                $this->title              = $this->get_option( 'title' );
    109109                $this->description        = $this->get_option( 'description' );
    110                 $this->enabled             = $this->get_option( 'enabled', 'no' );
     110                $this->enabled            = $this->get_option( 'enabled', 'no' );
    111111                $this->balance_due_status = $this->get_option( 'balance_due_status', 'on-hold' );
    112112
    … …  
    125125        public function init_form_fields() {
    126126                $this->form_fields = array(
    127                         'enabled'                            => array(
     127                        'enabled'                           => array(
    128128                                'title'   => __( 'Enable', 'mora-delivery-for-woocommerce' ),
    129129                                'type'    => 'checkbox',
    … …  
    131131                                'default' => 'no',
    132132                        ),
    133                         'title'                               => array(
     133                        'title'                             => array(
    134134                                'title'       => __( 'Title', 'mora-delivery-for-woocommerce' ),
    135135                                'type'        => 'text',
    … …  
    138138                                'desc_tip'    => true,
    139139                        ),
    140                         'description'                        => array(
     140                        'description'                       => array(
    141141                                'title'   => __( 'Description', 'mora-delivery-for-woocommerce' ),
    142142                                'type'    => 'textarea',
    143143                                'default' => __( 'Pay the delivery fee now by card, bank transfer, or USSD. Any goods balance is paid to the rider on delivery.', 'mora-delivery-for-woocommerce' ),
    144144                        ),
    145                         'restrict_to_lagos'                  => array(
     145                        'restrict_to_lagos'                 => array(
    146146                                'title'       => __( 'Restrict to Lagos', 'mora-delivery-for-woocommerce' ),
    147147                                'type'        => 'checkbox',
    … …  
    150150                                'description' => __( 'Off by default: this gateway otherwise applies to any order regardless of shipping method or destination. Turn this on if the store delivers with Mora nationwide but only wants this fee-now/collect-on-delivery flow offered within Lagos; an interstate order with Mora chosen still delivers, it just pays in full upfront through another payment method.', 'mora-delivery-for-woocommerce' ),
    151151                        ),
    152                         'balance_due_status'                 => array(
     152                        'balance_due_status'                => array(
    153153                                'title'       => __( 'Order status while a goods balance is due', 'mora-delivery-for-woocommerce' ),
    154154                                'type'        => 'select',
    … …  
    160160                                'description' => __( 'Used only while a goods balance remains after the delivery fee is collected; an order with nothing left owing always goes straight to Processing regardless of this setting. Choose Processing if this store\'s fulfilment/warehouse tooling triggers off that status rather than On hold.', 'mora-delivery-for-woocommerce' ),
    161161                        ),
    162                         'hold_minutes'                        => array(
     162                        'hold_minutes'                      => array(
    163163                                'title'             => __( 'Hold unpaid bank transfer and USSD orders for', 'mora-delivery-for-woocommerce' ),
    164164                                'type'              => 'number',
    … …  
    174174                                // no longer polled. Previously enforced nowhere, client or
    175175                                // server side.
    176                                 'custom_attributes' => array( 'min' => '1', 'max' => (string) self::MAX_POLL_MINUTES ),
     176                                'custom_attributes' => array(
     177                                        'min' => '1',
     178                                        'max' => (string) self::MAX_POLL_MINUTES,
     179                                ),
    177180                                'description'       => __( 'Minutes to hold an order open, and prevent WooCommerce\'s own unpaid-order sweeper from cancelling it, while waiting for a bank transfer or USSD payment to verify. After this, Mora marks the order failed itself.', 'mora-delivery-for-woocommerce' ),
    178181                                'default'           => '180',
    179182                                'desc_tip'          => true,
    180183                        ),
    181                         'psp_driver'                          => array(
     184                        'psp_driver'                        => array(
    182185                                'title'   => __( 'Payment provider', 'mora-delivery-for-woocommerce' ),
    183186                                'type'    => 'select',
    … …  
    187190                                'default' => 'paystack',
    188191                        ),
    189                         'paystack_override_heading'          => array(
     192                        'paystack_override_heading'         => array(
    190193                                'title'       => __( 'Paystack keys (optional)', 'mora-delivery-for-woocommerce' ),
    191194                                'type'        => 'title',
    192195                                'description' => __( 'Leave these blank to use the keys already configured under WooCommerce > Settings > Payments > Paystack. Only fill these in if this store does not have that plugin installed.', 'mora-delivery-for-woocommerce' ),
    193196                        ),
    194                         'paystack_override_testmode'         => array(
     197                        'paystack_override_testmode'        => array(
    195198                                'title'   => __( 'Test mode', 'mora-delivery-for-woocommerce' ),
    196199                                'type'    => 'checkbox',
    … …  
    198201                                'default' => 'no',
    199202                        ),
    200                         'paystack_override_test_secret_key'  => array(
     203                        'paystack_override_test_secret_key' => array(
    201204                                'title'   => __( 'Test secret key', 'mora-delivery-for-woocommerce' ),
    202205                                'type'    => 'password',
    203206                                'default' => '',
    204207                        ),
    205                         'paystack_override_live_secret_key'  => array(
     208                        'paystack_override_live_secret_key' => array(
    206209                                'title'   => __( 'Live secret key', 'mora-delivery-for-woocommerce' ),
    207210                                'type'    => 'password',
    … …  
    323326                        return false;
    324327                }
    325                 $state = WC()->customer->get_shipping_state() ?: WC()->customer->get_billing_state();
     328                $state = WC()->customer->get_shipping_state();
     329                if ( ! $state ) {
     330                        $state = WC()->customer->get_billing_state();
     331                }
    326332                return self::LAGOS_STATE_CODE === $state;
    327333        }
    … …  
    399405                                sprintf(
    400406                                        'Mora: %s initialize() failed for order #%d: %s',
    401                                         $driver->id(), $order->get_id(), isset( $result['error'] ) ? $result['error'] : 'unknown error'
     407                                        $driver->id(),
     408                                        $order->get_id(),
     409                                        isset( $result['error'] ) ? $result['error'] : 'unknown error'
    402410                                ),
    403411                                array( 'source' => 'mora' )
    … …  
    418426                $order->save();
    419427
    420                 $order->add_order_note( sprintf(
    421                         'Mora: delivery-fee payment of %s started via %s (reference %s)%s.',
    422                         wc_price( $amount ), $driver->label(), $reference,
    423                         $driver->is_testmode() ? ' [TEST MODE]' : ''
    424                 ) );
     428                $order->add_order_note(
     429                        sprintf(
     430                                'Mora: delivery-fee payment of %s started via %s (reference %s)%s.',
     431                                wc_price( $amount ),
     432                                $driver->label(),
     433                                $reference,
     434                                $driver->is_testmode() ? ' [TEST MODE]' : ''
     435                        )
     436                );
    425437
    426438                $this->schedule_poll( $order->get_id(), $reference, 0, 0 );
    … …  
    589601                                sprintf(
    590602                                        'Mora: gave up polling reference %s for order #%d after %d minutes, still unpaid.',
    591                                         $reference, $order_id, $elapsed_minutes
     603                                        $reference,
     604                                        $order_id,
     605                                        $elapsed_minutes
    592606                                ),
    593607                                array( 'source' => 'mora' )
    … …  
    649663                                sprintf(
    650664                                        'Mora: verified payment amount/currency mismatch for order #%d, reference %s: expected %s %.2f, got %s %.2f. Not completing.',
    651                                         $order->get_id(), $reference, $expected_currency, $expected_amount,
    652                                         isset( $result['currency'] ) ? $result['currency'] : '?', $result['amount_major']
     665                                        $order->get_id(),
     666                                        $reference,
     667                                        $expected_currency,
     668                                        $expected_amount,
     669                                        isset( $result['currency'] ) ? $result['currency'] : '?',
     670                                        $result['amount_major']
    653671                                ),
    654672                                array( 'source' => 'mora' )
    … …  
    664682                        $order->update_meta_data( '_mora_psp_verified_at', current_time( 'mysql', true ) );
    665683                        $order->save();
    666                         $order->add_order_note( sprintf(
    667                                 'Mora: payment of %s verified against reference %s, but this order is already %s. The customer has paid and needs a refund or a new order. Not resurrected automatically.',
    668                                 wc_price( $result['amount_major'] ), $reference, $order->get_status()
    669                         ) );
     684                        $order->add_order_note(
     685                                sprintf(
     686                                        'Mora: payment of %s verified against reference %s, but this order is already %s. The customer has paid and needs a refund or a new order. Not resurrected automatically.',
     687                                        wc_price( $result['amount_major'] ),
     688                                        $reference,
     689                                        $order->get_status()
     690                                )
     691                        );
    670692                        wc_get_logger()->error(
    671693                                sprintf(
    672694                                        'Mora: verified payment against a cancelled/refunded/failed order #%d, reference %s.',
    673                                         $order->get_id(), $reference
     695                                        $order->get_id(),
     696                                        $reference
    674697                                ),
    675698                                array( 'source' => 'mora' )
    … …  
    701724                // participates in, so noting "fee collected" ahead of a status change
    702725                // that might still fail would describe something that didn't happen.
    703                 $order->add_order_note( sprintf(
    704                         'Mora: delivery-fee payment verified via %s (reference %s). Collected %s; %s owed on delivery.',
    705                         $driver->label(), $reference, wc_price( $result['amount_major'] ),
    706                         $goods_balance > 0 ? wc_price( $goods_balance ) : __( 'nothing', 'mora-delivery-for-woocommerce' )
    707                 ) );
     726                $order->add_order_note(
     727                        sprintf(
     728                                'Mora: delivery-fee payment verified via %s (reference %s). Collected %s; %s owed on delivery.',
     729                                $driver->label(),
     730                                $reference,
     731                                wc_price( $result['amount_major'] ),
     732                                $goods_balance > 0 ? wc_price( $goods_balance ) : __( 'nothing', 'mora-delivery-for-woocommerce' )
     733                        )
     734                );
    708735        }
    709736
    … …  
    733760                        return;
    734761                }
    735                 echo '<p>' . esc_html( sprintf(
     762                echo '<p>' . esc_html(
     763                        sprintf(
    736764                        /* translators: %s: goods balance still owed, formatted as a price. */
    737                         __( 'Your delivery fee has been paid. %s is still owed for the goods, payable to the rider on delivery.', 'mora-delivery-for-woocommerce' ),
    738                         wp_strip_all_tags( wc_price( (float) $balance ) )
    739                 ) ) . '</p>';
     765                                __( 'Your delivery fee has been paid. %s is still owed for the goods, payable to the rider on delivery.', 'mora-delivery-for-woocommerce' ),
     766                                wp_strip_all_tags( wc_price( (float) $balance ) )
     767                        )
     768                ) . '</p>';
    740769        }
    741770
    … …  
    759788                        if ( isset( $_GET[ self::COD_DISABLED_FLAG ] ) && '1' === sanitize_text_field( wp_unslash( $_GET[ self::COD_DISABLED_FLAG ] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification
    760789                                echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__(
    761                                         'WooCommerce\'s stock Cash on Delivery gateway has been disabled.', 'mora-delivery-for-woocommerce'
     790                                        'WooCommerce\'s stock Cash on Delivery gateway has been disabled.',
     791                                        'mora-delivery-for-woocommerce'
    762792                                ) . '</p></div>';
    763793                        } elseif ( $this->is_cod_enabled() ) {
  • mora-delivery-for-woocommerce/tags/0.7.79/includes/class-mora-paystack-driver.php

    r3746963 r3749063  
    7777                if ( $has_override ) {
    7878                        $key = $this->override_testmode ? $this->override_test_secret_key : $this->override_live_secret_key;
    79                         return array( 'key' => $key, 'testmode' => $this->override_testmode );
    80                 }
    81 
    82                 $paystack_settings = get_option( 'woocommerce_paystack_settings', array() );
     79                        return array(
     80                                'key'      => $key,
     81                                'testmode' => $this->override_testmode,
     82                        );
     83                }
     84
     85                $paystack_settings  = get_option( 'woocommerce_paystack_settings', array() );
    8386                $inherited_testmode = isset( $paystack_settings['testmode'] ) && 'yes' === $paystack_settings['testmode'];
    84                 $key = $inherited_testmode
     87                $key                = $inherited_testmode
    8588                        ? ( isset( $paystack_settings['test_secret_key'] ) ? $paystack_settings['test_secret_key'] : '' )
    8689                        : ( isset( $paystack_settings['live_secret_key'] ) ? $paystack_settings['live_secret_key'] : '' );
    8790
    88                 return array( 'key' => $key, 'testmode' => $inherited_testmode );
     91                return array(
     92                        'key'      => $key,
     93                        'testmode' => $inherited_testmode,
     94                );
    8995        }
    9096
    … …  
    9298                $keys = $this->resolve_keys();
    9399                if ( '' === trim( $keys['key'] ) ) {
    94                         return array( 'ok' => false, 'error' => 'Paystack secret key is not configured.' );
     100                        return array(
     101                                'ok'    => false,
     102                                'error' => 'Paystack secret key is not configured.',
     103                        );
    95104                }
    96105
    … …  
    119128
    120129                if ( is_wp_error( $response ) ) {
    121                         return array( 'ok' => false, 'error' => $response->get_error_message() );
     130                        return array(
     131                                'ok'    => false,
     132                                'error' => $response->get_error_message(),
     133                        );
    122134                }
    123135
    … …  
    126138
    127139                if ( 200 !== $status || empty( $data['status'] ) || empty( $data['data']['authorization_url'] ) ) {
    128                         return array( 'ok' => false, 'error' => $this->error_message( $data, $status ) );
    129                 }
    130 
    131                 return array( 'ok' => true, 'redirect_url' => $data['data']['authorization_url'] );
     140                        return array(
     141                                'ok'    => false,
     142                                'error' => $this->error_message( $data, $status ),
     143                        );
     144                }
     145
     146                return array(
     147                        'ok'           => true,
     148                        'redirect_url' => $data['data']['authorization_url'],
     149                );
    132150        }
    133151
    … …  
    135153                $keys = $this->resolve_keys();
    136154                if ( '' === trim( $keys['key'] ) ) {
    137                         return array( 'ok' => false, 'error' => 'Paystack secret key is not configured.' );
     155                        return array(
     156                                'ok'    => false,
     157                                'error' => 'Paystack secret key is not configured.',
     158                        );
    138159                }
    139160
    … …  
    147168
    148169                if ( is_wp_error( $response ) ) {
    149                         return array( 'ok' => false, 'error' => $response->get_error_message() );
     170                        return array(
     171                                'ok'    => false,
     172                                'error' => $response->get_error_message(),
     173                        );
    150174                }
    151175
    … …  
    154178
    155179                if ( 200 !== $status || empty( $data['status'] ) || ! isset( $data['data'] ) ) {
    156                         return array( 'ok' => false, 'error' => $this->error_message( $data, $status ) );
     180                        return array(
     181                                'ok'    => false,
     182                                'error' => $this->error_message( $data, $status ),
     183                        );
    157184                }
    158185
  • mora-delivery-for-woocommerce/tags/0.7.79/includes/class-mora-sentry-reporter.php

    r3746963 r3749063  
    6464        // elsewhere in this plugin (order meta, checkout fields).
    6565        const PII_KEYS = array(
    66                 'customer_name', 'customer_email', 'customer_phone', 'phone', 'email',
    67                 'address', 'address_1', 'address_2', 'billing_email', 'billing_phone',
    68                 'first_name', 'last_name',
     66                'customer_name',
     67                'customer_email',
     68                'customer_phone',
     69                'phone',
     70                'email',
     71                'address',
     72                'address_1',
     73                'address_2',
     74                'billing_email',
     75                'billing_phone',
     76                'first_name',
     77                'last_name',
    6978        );
    7079
    … …  
    107116                        $parsed['store_url'],
    108117                        array(
    109                                 'headers'   => array(
     118                                'headers'  => array(
    110119                                        'Content-Type'  => 'application/json',
    111120                                        'X-Sentry-Auth' => self::auth_header( $parsed['public_key'] ),
    112121                                ),
    113                                 'body'      => wp_json_encode( $event ),
    114                                 'timeout'   => self::TIMEOUT_SECONDS,
    115                                 'blocking'  => false,
     122                                'body'     => wp_json_encode( $event ),
     123                                'timeout'  => self::TIMEOUT_SECONDS,
     124                                'blocking' => false,
    116125                        )
    117126                );
  • mora-delivery-for-woocommerce/tags/0.7.79/includes/class-mora-shipping-method.php

    r3746963 r3749063  
    7373                $this->enabled = $this->get_option( 'enabled', 'yes' );
    7474
    75                 $credentials         = Mora_Admin_Settings::site_wide_credentials();
     75                $credentials        = Mora_Admin_Settings::site_wide_credentials();
    7676                $this->api_base_url = untrailingslashit( $credentials['api_base_url'] );
    77                 $this->api_key       = $credentials['api_key'];
     77                $this->api_key      = $credentials['api_key'];
    7878
    7979                add_action( 'woocommerce_update_options_shipping_' . $this->id, array( $this, 'process_admin_options' ) );
    … …  
    120120                $locator_on = Mora_Admin_Settings::is_places_locator_enabled();
    121121                $location   = $locator_on ? Mora_Checkout::get_pinned_location() : array();
    122                 $lat      = isset( $location['lat'] ) ? $location['lat'] : null;
    123                 $lon      = isset( $location['lon'] ) ? $location['lon'] : null;
     122                $lat        = isset( $location['lat'] ) ? $location['lat'] : null;
     123                $lon        = isset( $location['lon'] ) ? $location['lon'] : null;
    124124
    125125                $has_coordinates = ( null !== $lat && null !== $lon );
    … …  
    129129                // here either when there is a pin.
    130130                $address = $has_coordinates ? null : $this->customer_address_text( $package, $state_name );
    131                 $quote = Mora_Api_Client::get_quote( $this->api_base_url, $this->api_key, $state_name, $lat, $lon, $address );
     131                $quote   = Mora_Api_Client::get_quote( $this->api_base_url, $this->api_key, $state_name, $lat, $lon, $address );
    132132
    133133                // Whether the customer has already given us an address to work from,
  • mora-delivery-for-woocommerce/tags/0.7.79/includes/class-mora-webhook-receiver.php

    r3746963 r3749063  
    7979                if ( '' === $secret ) {
    8080                        self::log_throttled( 'error', 'Mora: webhook received but no webhook secret is configured on this site.', 'no_secret' );
    81                         return new WP_REST_Response( array( 'ok' => false, 'error' => 'webhook not configured' ), 501 );
     81                        return new WP_REST_Response(
     82                                array(
     83                                        'ok'    => false,
     84                                        'error' => 'webhook not configured',
     85                                ),
     86                                501
     87                        );
    8288                }
    8389
    … …  
    8793                // number formatting). Same reasoning as the documented-but-unbuilt
    8894                // Paystack signature check elsewhere in this codebase.
    89                 $raw_body = $request->get_body();
     95                $raw_body           = $request->get_body();
    9096                $received_signature = (string) $request->get_header( 'x_mora_webhook_signature' );
    9197                if ( '' === $received_signature ) {
    92                         return new WP_REST_Response( array( 'ok' => false, 'error' => 'missing signature' ), 401 );
     98                        return new WP_REST_Response(
     99                                array(
     100                                        'ok'    => false,
     101                                        'error' => 'missing signature',
     102                                ),
     103                                401
     104                        );
    93105                }
    94106
    … …  
    96108                if ( ! hash_equals( $expected_signature, $received_signature ) ) {
    97109                        self::log_throttled( 'warning', 'Mora: webhook signature did not match.', 'bad_signature' );
    98                         return new WP_REST_Response( array( 'ok' => false, 'error' => 'invalid signature' ), 401 );
     110                        return new WP_REST_Response(
     111                                array(
     112                                        'ok'    => false,
     113                                        'error' => 'invalid signature',
     114                                ),
     115                                401
     116                        );
    99117                }
    100118
    101119                $payload = json_decode( $raw_body, true );
    102120                if ( ! is_array( $payload ) || empty( $payload['merchant_order_id'] ) || empty( $payload['status'] ) ) {
    103                         return new WP_REST_Response( array( 'ok' => false, 'error' => 'malformed payload' ), 400 );
     121                        return new WP_REST_Response(
     122                                array(
     123                                        'ok'    => false,
     124                                        'error' => 'malformed payload',
     125                                ),
     126                                400
     127                        );
    104128                }
    105129
    … …  
    113137                                array( 'source' => 'mora' )
    114138                        );
    115                         return new WP_REST_Response( array( 'ok' => false, 'error' => 'order not found' ), 404 );
     139                        return new WP_REST_Response(
     140                                array(
     141                                        'ok'    => false,
     142                                        'error' => 'order not found',
     143                                ),
     144                                404
     145                        );
    116146                }
    117147
    … …  
    130160                                array( 'source' => 'mora' )
    131161                        );
    132                         return new WP_REST_Response( array( 'ok' => false, 'error' => 'order_id mismatch' ), 409 );
     162                        return new WP_REST_Response(
     163                                array(
     164                                        'ok'    => false,
     165                                        'error' => 'order_id mismatch',
     166                                ),
     167                                409
     168                        );
    133169                }
    134170
  • mora-delivery-for-woocommerce/tags/0.7.79/mora-delivery-for-woocommerce.php

    r3748164 r3749063  
    33 * Plugin Name: Mora Delivery for WooCommerce
    44 * Description: Live Mora delivery quotes at checkout, backed by mora-api's GET /v1/quote, a goods-on-delivery payment gateway that charges the delivery fee upfront, and pushes placed orders on to Mora.
    5  * Version: 0.7.78
     5 * Version: 0.7.79
    66 * Requires at least: 6.0
    77 * Requires PHP: 7.4
    … …  
    2121}
    2222
    23 define( 'MORA_WC_VERSION', '0.7.78' );
     23define( 'MORA_WC_VERSION', '0.7.79' );
    2424define( 'MORA_WC_PLUGIN_FILE', __FILE__ );
    2525define( 'MORA_WC_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
  • mora-delivery-for-woocommerce/tags/0.7.79/readme.txt

    r3748164 r3749063  
    55Tested up to: 7.1
    66Requires PHP: 7.4
    7 Stable tag: 0.7.78
     7Stable tag: 0.7.79
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
    … …  
    3131This plugin connects to the following services to provide its features. None of them run unless the matching feature is actually configured and in use.
    3232
    33 **Mora Logistics API (mora-api)**: Mora's own backend, used to price delivery quotes, process the delivery-fee payment gateway's PSP verification, and push/sync placed orders. When a quote is requested, this plugin sends the destination state, and, where available, the customer's pinned coordinates or typed address, to `https://api.moralogistics.com`. When an order is placed, order, product, and customer contact details are sent there so Mora can fulfil the delivery. This is Mora's own first-party service, governed by the same merchant agreement a Mora Logistics merchant signs; see Mora Logistics' Terms of Service (https://moralogistics.com/terms) and Privacy Policy (https://moralogistics.com/privacy-policy).
     33**Mora Logistics API (mora-api)**: Mora's own backend, used to price delivery quotes and to push and sync placed orders. When a quote is requested, this plugin sends the destination state, and, where available, the customer's pinned coordinates or typed address, to the Mora API address set in the plugin's settings (Mora's production service is `https://api.moralogistics.com`). When an order is placed, order, product, and customer contact details are sent there so Mora can fulfil the delivery. This is Mora's own first-party service, governed by the same merchant agreement a Mora Logistics merchant signs; see Mora Logistics' Terms of Service (https://moralogistics.com/terms) and Privacy Policy (https://moralogistics.com/privacy-policy).
    3434
    3535**Paystack**: used only by this plugin's own payment gateway (optional, merchant-enabled), through the merchant's own Paystack account. The order's delivery-fee amount, currency, and a payment reference are sent to Paystack to process the charge (sent to `https://api.paystack.co` when the customer places an order with this gateway, and again to verify the payment); no card details pass through this plugin or site, Paystack's own hosted page handles that directly. See Paystack's Terms of Use (https://paystack.com/terms) and Merchant Privacy Policy (https://paystack.com/privacy/merchant).
    … …  
    7171
    7272== Changelog ==
     73
     74= 0.7.79 =
     75* Fixed: hub-pickup orders from the block checkout (WooCommerce Local Pickup) were never sent to Mora. They are now, like classic-checkout pickup orders.
     76* Docs: External Services no longer says payments are verified through Mora's API (they are verified directly with Paystack), and names the configured API address.
    7377
    7478= 0.7.78 =
  • mora-delivery-for-woocommerce/tags/0.7.79/uninstall.php

    r3746963 r3749063  
    2121
    2222// This plugin's own site-wide options.
    23 $options = array(
     23$mora_wc_options = array(
    2424        'mora_wc_places_api_key',
    2525        'mora_wc_reorder_core_fields',
    … …  
    3838        'woocommerce_mora_settings',
    3939);
    40 foreach ( $options as $option ) {
    41         delete_option( $option );
     40foreach ( $mora_wc_options as $mora_wc_option ) {
     41        delete_option( $mora_wc_option );
    4242}
    4343
  • mora-delivery-for-woocommerce/trunk/includes/class-mora-admin-settings.php

    r3746963 r3749063  
    400400                }
    401401                self::$resolving_site_wide_credentials = true;
    402                 $scanned = self::scan_zone_instances_for_credentials();
     402                $scanned                               = self::scan_zone_instances_for_credentials();
    403403                self::$resolving_site_wide_credentials = false;
    404404                if ( '' === $scanned['api_key'] ) {
  • mora-delivery-for-woocommerce/trunk/includes/class-mora-blocks-payment-method.php

    r3746963 r3749063  
    6969        public function initialize() {
    7070                $this->settings = get_option( 'woocommerce_' . $this->name . '_settings', array() );
    71                 $gateways      = WC()->payment_gateways() ? WC()->payment_gateways()->payment_gateways() : array();
    72                 $this->gateway = isset( $gateways[ $this->name ] ) ? $gateways[ $this->name ] : null;
     71                $gateways       = WC()->payment_gateways() ? WC()->payment_gateways()->payment_gateways() : array();
     72                $this->gateway  = isset( $gateways[ $this->name ] ) ? $gateways[ $this->name ] : null;
    7373        }
    7474
    … …  
    9090        public function get_payment_method_data() {
    9191                return array(
    92                         'title'          => $this->gateway ? $this->gateway->title : '',
    93                         'description'    => $this->gateway ? $this->gateway->description : '',
     92                        'title'           => $this->gateway ? $this->gateway->title : '',
     93                        'description'     => $this->gateway ? $this->gateway->description : '',
    9494                        // Static per page load (an admin setting, not cart state), safe
    9595                        // for canMakePayment() to branch on directly; the live destination
  • mora-delivery-for-woocommerce/trunk/includes/class-mora-checkout-experiment.php

    r3746963 r3749063  
    176176                }
    177177
    178                 $due_today = (float) WC()->cart->get_shipping_total() + (float) WC()->cart->get_shipping_tax();
     178                $due_today       = (float) WC()->cart->get_shipping_total() + (float) WC()->cart->get_shipping_tax();
    179179                $due_on_delivery = (float) WC()->cart->get_total( 'edit' ) - $due_today;
    180180                if ( $due_on_delivery < 0 ) {
  • mora-delivery-for-woocommerce/trunk/includes/class-mora-checkout.php

    r3746963 r3749063  
    3535class Mora_Checkout {
    3636
    37         const NONCE_ACTION = 'mora_wc_set_coordinates';
     37        const NONCE_ACTION            = 'mora_wc_set_coordinates';
    3838        const COORDS_TRANSIENT_PREFIX = 'mora_coords_';
    3939
    4040        // See get_places_api_key()'s own docblock for why this is cached at all,
    4141        // and why success and failure carry different TTLs.
    42         const PLACES_KEY_TRANSIENT = 'mora_wc_places_key_cache';
    43         const PLACES_KEY_CACHE_TTL = 12 * HOUR_IN_SECONDS;
     42        const PLACES_KEY_TRANSIENT         = 'mora_wc_places_key_cache';
     43        const PLACES_KEY_CACHE_TTL         = 12 * HOUR_IN_SECONDS;
    4444        const PLACES_KEY_FAILURE_CACHE_TTL = 5 * MINUTE_IN_SECONDS;
    4545
    … …  
    127127         */
    128128        public static function locator_script_data() {
     129                $ng_states = WC()->countries->get_states( 'NG' );
    129130                return array(
    130131                        'placesApiKey' => self::get_places_api_key(),
    … …  
    136137                        // code => name list classic's own <select> options already reflect,
    137138                        // sourced from the same place WooCommerce itself does.
    138                         'ngStates'     => WC()->countries->get_states( 'NG' ) ?: array(),
     139                        'ngStates'     => $ng_states ? $ng_states : array(),
    139140                        'i18n'         => array(
    140141                                // The widget is a locator (sets coordinates, city, state, LGA for
    … …  
    145146                                // phase adds a real, required field literally called "Landmark", so the
    146147                                // widget can no longer describe itself the same way.
    147                                 'locatorLabel' => __( 'Find your delivery address', 'mora-delivery-for-woocommerce' ),
     148                                'locatorLabel'       => __( 'Find your delivery address', 'mora-delivery-for-woocommerce' ),
    148149                                // Measured on 20 real Lagos addresses: 18/20 resolved to door level when
    149150                                // the query contained a house number. That instruction is the single most
    … …  
    153154                                // (New) release is not confirmed against Google's primary docs from this
    154155                                // environment).
    155                                 'locatorHint'  => __( 'Start with your house number, like 12 Allen Avenue. We use this to work out your delivery fee.', 'mora-delivery-for-woocommerce' ),
     156                                'locatorHint'        => __( 'Start with your house number, like 12 Allen Avenue. We use this to work out your delivery fee.', 'mora-delivery-for-woocommerce' ),
    156157                                // A generic example address, deliberately not the Mora hub's own street
    157158                                // address (16 Adebiyi Street): that's a real, specific location tied to the
    … …  
    295296                // two days for a guest. The pin has no reason to outlive the session it belongs to.
    296297                $ttl = is_user_logged_in() ? WEEK_IN_SECONDS : 2 * DAY_IN_SECONDS;
    297                 set_transient( $key, array(
    298                         'lat'    => $lat,
    299                         'lon'    => $lon,
    300                         'lga'    => $lga,
    301                         'pinned' => $pinned,
    302                 ), $ttl );
     298                set_transient(
     299                        $key,
     300                        array(
     301                                'lat'    => $lat,
     302                                'lon'    => $lon,
     303                                'lga'    => $lga,
     304                                'pinned' => $pinned,
     305                        ),
     306                        $ttl
     307                );
    303308        }
    304309
  • mora-delivery-for-woocommerce/trunk/includes/class-mora-fields.php

    r3746963 r3749063  
    9999                                'classic_type'      => 'text',
    100100                                'required_for_mora' => false, // always optional, Mora rate or not; see the class
    101                                                                // docblock for why this was changed.
     101                                                                                                // docblock for why this was changed.
    102102                                'label'             => __( 'Landmark', 'mora-delivery-for-woocommerce' ),
    103103                                'placeholder'       => __( 'A shop, school or building we can find', 'mora-delivery-for-woocommerce' ),
    … …  
    189189
    190190        /**
     191         * WooCommerce's built-in pickup method ids: `local_pickup` on classic
     192         * checkout, `pickup_location` on Blocks checkout (WooCommerce's newer
     193         * Local Pickup). Both carry Mora's hub-pickup fee.
     194         */
     195        const PICKUP_METHOD_IDS = array( 'local_pickup', 'pickup_location' );
     196
     197        /**
     198         * Whether a shipping method id is one of WooCommerce's pickup methods.
     199         *
     200         * @param string $method_id Shipping method id.
     201         * @return bool
     202         */
     203        public static function is_pickup_method( $method_id ) {
     204                return in_array( $method_id, self::PICKUP_METHOD_IDS, true );
     205        }
     206
     207        /**
    191208         * has_mora_shipping_line()'s sibling for hub pickup: does this order use
    192          * WooCommerce's own built-in Local Pickup method (method id
    193          * `local_pickup`, not a Mora-owned one). Mora_Order_Push::can_push()
     209         * one of WooCommerce's own built-in pickup methods (PICKUP_METHOD_IDS,
     210         * not a Mora-owned one). Mora_Order_Push::can_push()
    194211         * reads this to also push a pickup order, alongside a Mora delivery
    195212         * one: the customer collects at the Mora hub instead of a rider
    … …  
    199216        public static function has_local_pickup_line( $order ) {
    200217                foreach ( $order->get_items( 'shipping' ) as $item ) {
    201                         if ( 'local_pickup' === $item->get_method_id() ) {
     218                        if ( self::is_pickup_method( $item->get_method_id() ) ) {
    202219                                return true;
    203220                        }
    … …  
    252269         * Landmark is never enforced here, on any rate; see the class docblock for why.
    253270         */
    254         public static function validate_blocks_phone_and_landmark( $context, $result ) {
     271        public static function validate_blocks_phone_and_landmark( $context, $result ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed -- $result is part of the hook's signature.
    255272                $chosen_methods = WC()->session ? WC()->session->get( 'chosen_shipping_methods', array() ) : array();
    256273                if ( ! self::is_mora_rate_chosen( $chosen_methods ) ) {
  • mora-delivery-for-woocommerce/trunk/includes/class-mora-leads-admin.php

    r3746963 r3749063  
    1717
    1818        const EXPORT_ACTION = 'mora_wc_leads_export';
     19
     20        // Shown in a list column with no value.
     21        const EMPTY_CELL = '-';
    1922
    2023        /**
    … …  
    8083        }
    8184
    82         public static function define_columns( $columns ) {
     85        public static function define_columns( $columns ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- filter signature; the default columns are replaced, not extended.
    8386                return array(
    8487                        'cb'                 => '<input type="checkbox" />',
    … …  
    9598
    9699        /**
     100         * A column value, or the empty-cell placeholder when it is empty.
     101         *
     102         * @param mixed $value Stored meta value.
     103         * @return mixed
     104         */
     105        private static function cell( $value ) {
     106                return $value ? $value : self::EMPTY_CELL;
     107        }
     108
     109        /**
    97110         * Every column but Cart is plain text, always esc_html()'d. The Cart
    98111         * column is the one exception: wc_price() returns real markup (a
    … …  
    107120                        case 'mora_lead_name':
    108121                                $name = trim( get_post_meta( $post_id, Mora_Leads::META_FIRST_NAME, true ) . ' ' . get_post_meta( $post_id, Mora_Leads::META_LAST_NAME, true ) );
    109                                 echo esc_html( $name ?: '—' );
     122                                echo esc_html( self::cell( $name ) );
    110123                                break;
    111124                        case 'mora_lead_phone':
    112                                 echo esc_html( get_post_meta( $post_id, Mora_Leads::META_PHONE, true ) ?: '—' );
     125                                echo esc_html( self::cell( get_post_meta( $post_id, Mora_Leads::META_PHONE, true ) ) );
    113126                                break;
    114127                        case 'mora_lead_email':
    115                                 echo esc_html( get_post_meta( $post_id, Mora_Leads::META_EMAIL, true ) ?: '—' );
     128                                echo esc_html( self::cell( get_post_meta( $post_id, Mora_Leads::META_EMAIL, true ) ) );
    116129                                break;
    117130                        case 'mora_lead_city':
    118                                 echo esc_html( get_post_meta( $post_id, Mora_Leads::META_CITY, true ) ?: '—' );
     131                                echo esc_html( self::cell( get_post_meta( $post_id, Mora_Leads::META_CITY, true ) ) );
    119132                                break;
    120133                        case 'mora_lead_cart':
    … …  
    124137                                break;
    125138                        case 'mora_lead_products':
    126                                 echo esc_html( get_post_meta( $post_id, Mora_Leads::META_PRODUCTS, true ) ?: '—' );
     139                                echo esc_html( self::cell( get_post_meta( $post_id, Mora_Leads::META_PRODUCTS, true ) ) );
    127140                                break;
    128141                        case 'mora_lead_seen':
    129142                                $last_seen = (int) get_post_meta( $post_id, Mora_Leads::META_LAST_SEEN, true );
    130                                 echo esc_html( $last_seen ? date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $last_seen ) : '—' );
     143                                echo esc_html( $last_seen ? date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $last_seen ) : self::EMPTY_CELL );
    131144                                break;
    132145                        case 'mora_lead_status':
    133                                 $status  = get_post_meta( $post_id, Mora_Leads::META_STATUS, true );
     146                                $status   = get_post_meta( $post_id, Mora_Leads::META_STATUS, true );
    134147                                $order_id = (int) get_post_meta( $post_id, Mora_Leads::META_ORDER_ID, true );
    135148                                if ( Mora_Leads::STATUS_CONVERTED === $status && $order_id && function_exists( 'wc_get_order' ) && wc_get_order( $order_id ) ) {
    … …  
    148161         * (a bare WooCommerce → Leads click, never a query string).
    149162         */
    150         public static function status_filter_views( $views ) {
     163        public static function status_filter_views( $views ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- filter signature; the default views are replaced, not extended.
    151164                $current = isset( $_GET['lead_status'] ) ? sanitize_key( wp_unslash( $_GET['lead_status'] ) ) : 'open'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
    152165
    … …  
    159172                $new_views = array();
    160173                foreach ( $tabs as $key => $label ) {
    161                         $count = self::count_leads_for_status( $key );
    162                         $url   = add_query_arg(
     174                        $count             = self::count_leads_for_status( $key );
     175                        $url               = add_query_arg(
    163176                                array(
    164177                                        'post_type'   => Mora_Leads::POST_TYPE,
    … …  
    167180                                admin_url( 'edit.php' )
    168181                        );
    169                         $class = $current === $key ? ' class="current"' : '';
     182                        $class             = $current === $key ? ' class="current"' : '';
    170183                        $new_views[ $key ] = sprintf(
    171184                                '<a href="%s"%s>%s <span class="count">(%d)</span></a>',
    … …  
    347360                        // computed once, at capture time, and is what conversion matching
    348361                        // also reads, so export and matching can never quietly disagree.
    349                         $phone      = get_post_meta( $lead_id, Mora_Leads::META_PHONE_NORMALIZED, true );
     362                        $phone = get_post_meta( $lead_id, Mora_Leads::META_PHONE_NORMALIZED, true );
    350363                        fputcsv(
    351364                                $out,
  • mora-delivery-for-woocommerce/trunk/includes/class-mora-leads.php

    r3746963 r3749063  
    4545        const POST_TYPE = 'mora_lead';
    4646
    47         const META_SESSION_ID  = '_mora_lead_session_id';
    48         const META_FIRST_NAME  = '_mora_lead_first_name';
    49         const META_LAST_NAME   = '_mora_lead_last_name';
     47        const META_SESSION_ID = '_mora_lead_session_id';
     48        const META_FIRST_NAME = '_mora_lead_first_name';
     49        const META_LAST_NAME  = '_mora_lead_last_name';
    5050        // As the customer typed it (local format, e.g. 08012345678): what the
    5151        // merchant searches by and sees in the admin list. META_PHONE_NORMALIZED
    … …  
    5555        const META_PHONE            = '_mora_lead_phone';
    5656        const META_PHONE_NORMALIZED = '_mora_lead_phone_normalized';
    57         const META_EMAIL       = '_mora_lead_email';
    58         const META_CITY        = '_mora_lead_city';
    59         const META_STATE       = '_mora_lead_state';
    60         const META_CART_COUNT  = '_mora_lead_cart_count';
    61         const META_CART_TOTAL  = '_mora_lead_cart_total';
     57        const META_EMAIL            = '_mora_lead_email';
     58        const META_CITY             = '_mora_lead_city';
     59        const META_STATE            = '_mora_lead_state';
     60        const META_CART_COUNT       = '_mora_lead_cart_count';
     61        const META_CART_TOTAL       = '_mora_lead_cart_total';
    6262        // "Name (SKU), Name (SKU)..." for the whole cart at capture time, not
    6363        // one line item: a lead can span multiple products, the same reason
    6464        // cart_count/cart_total summarize the whole cart rather than one item.
    65         const META_PRODUCTS    = '_mora_lead_products';
    66         const META_FIRST_SEEN  = '_mora_lead_first_seen';
    67         const META_LAST_SEEN   = '_mora_lead_last_seen';
    68         const META_STATUS      = '_mora_lead_status';
    69         const META_ORDER_ID    = '_mora_lead_order_id';
     65        const META_PRODUCTS   = '_mora_lead_products';
     66        const META_FIRST_SEEN = '_mora_lead_first_seen';
     67        const META_LAST_SEEN  = '_mora_lead_last_seen';
     68        const META_STATUS     = '_mora_lead_status';
     69        const META_ORDER_ID   = '_mora_lead_order_id';
    7070
    7171        const STATUS_OPEN      = 'open';
    7272        const STATUS_CONVERTED = 'converted';
    7373
    74         const THROTTLE_SECONDS      = 60;
     74        const THROTTLE_SECONDS       = 60;
    7575        const RETENTION_DAYS_DEFAULT = 90;
    76         const PURGE_CRON_HOOK       = 'mora_wc_purge_leads';
     76        const PURGE_CRON_HOOK        = 'mora_wc_purge_leads';
    7777        // A daily cron deleting an unbounded set in one request will eventually
    7878        // time out; a capped, oldest-first batch clears a real backlog over a
    7979        // few days instead of failing outright on the day it's biggest.
    80         const PURGE_BATCH_SIZE      = 200;
     80        const PURGE_BATCH_SIZE = 200;
    8181
    8282        // Only these three WooCommerce order statuses mean "this order is real
    … …  
    177177         * pollutes the leads list with the merchant's own sessions.
    178178         */
    179         public static function capture_from_store_api( $customer, $request ) {
     179        public static function capture_from_store_api( $customer, $request ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed -- $request is part of the hook's signature.
    180180                if ( ! $customer instanceof WC_Customer ) {
    181181                        return;
    … …  
    243243                        // Shipping-preferred-falls-back-to-billing, the same idiom
    244244                        // Mora_Order_Push::build_payload() already uses for city/state.
    245                         'city'       => $customer->get_shipping_city() ?: $customer->get_billing_city(),
    246                         'state'      => $customer->get_shipping_state() ?: $customer->get_billing_state(),
     245                        'city'       => $customer->get_shipping_city() ? $customer->get_shipping_city() : $customer->get_billing_city(),
     246                        'state'      => $customer->get_shipping_state() ? $customer->get_shipping_state() : $customer->get_billing_state(),
    247247                        'cart_count' => WC()->cart ? WC()->cart->get_cart_contents_count() : 0,
    248248                        // 'edit' context: the raw numeric total, not a pre-formatted
    … …  
    272272                                continue;
    273273                        }
    274                         $sku    = $product->get_sku();
     274                        $sku     = $product->get_sku();
    275275                        $names[] = $sku ? $product->get_name() . ' (' . $sku . ')' : $product->get_name();
    276276                }
    … …  
    422422        }
    423423
    424         public static function reopen_lead_for_order( $order_id, $order ) {
     424        public static function reopen_lead_for_order( $order_id, $order ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed -- $order is part of the hook's signature.
    425425                $lead_ids = get_posts(
    426426                        array(
    … …  
    582582         * eraser keeps calling this until a page genuinely comes back empty.
    583583         */
    584         public static function privacy_eraser( $email_address, $page = 1 ) {
     584        public static function privacy_eraser( $email_address, $page = 1 ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed -- eraser callback signature; $page deliberately unused, see above.
    585585                $per_page = 50;
    586586                $lead_ids = get_posts(
  • mora-delivery-for-woocommerce/trunk/includes/class-mora-locator-block.php

    r3746963 r3749063  
    5656        // Naming mirrors Mora_Payment_Gateway::COD_DISABLE_ACTION, the existing
    5757        // one-click-admin-notice pattern this feature follows.
    58         const ADD_ACTION               = 'mora_wc_add_locator_block';
    59         const RESULT_TRANSIENT_PREFIX  = 'mora_wc_locator_result_';
     58        const ADD_ACTION              = 'mora_wc_add_locator_block';
     59        const RESULT_TRANSIENT_PREFIX = 'mora_wc_locator_result_';
    6060
    6161        public static function init() {
  • mora-delivery-for-woocommerce/trunk/includes/class-mora-order-push.php

    r3748164 r3749063  
    120120                        return true; // hub pickup: no destination to validate
    121121                }
    122                 $country = $order->get_shipping_country() ?: $order->get_billing_country();
     122                $country = self::first_filled( $order->get_shipping_country(), $order->get_billing_country() );
    123123                if ( 'NG' !== $country ) {
    124124                        $reason = sprintf(
    … …  
    763763                // A Mora delivery line always wins if present (matches can_push()'s own
    764764                // priority): only treat this as a hub-pickup order when there is no
    765                 // Mora line at all. WooCommerce's built-in Local Pickup method
    766                 // (`local_pickup`) is what carries the pickup fee in that case; the
     765                // Mora line at all. WooCommerce's built-in pickup method
     766                // (Mora_Fields::PICKUP_METHOD_IDS) carries the pickup fee in that case; the
    767767                // customer collects at the Mora hub, so there is no delivery address
    768768                // to send at all.
    769                 $is_pickup      = empty( $mora_lines );
    770                 $expected_method = $is_pickup ? 'local_pickup' : 'mora';
    771                 $fee_lines      = $is_pickup
     769                $is_pickup   = empty( $mora_lines );
     770                $fee_lines   = $is_pickup
    772771                        ? array_values(
    773772                                array_filter(
    774773                                        $shipping_lines,
    775774                                        function ( $item ) {
    776                                                 return 'local_pickup' === $item->get_method_id();
     775                                                return Mora_Fields::is_pickup_method( $item->get_method_id() );
    777776                                        }
    778777                                )
    779778                        )
    780779                        : $mora_lines;
    781                 $other_lines    = array_values(
     780                $other_lines = array_values(
    782781                        array_filter(
    783782                                $shipping_lines,
    784                                 function ( $item ) use ( $expected_method ) {
    785                                         return $expected_method !== $item->get_method_id();
     783                                function ( $item ) use ( $is_pickup ) {
     784                                        $id = $item->get_method_id();
     785                                        return $is_pickup ? ! Mora_Fields::is_pickup_method( $id ) : 'mora' !== $id;
    786786                                }
    787787                        )
    … …  
    801801                }
    802802
    803                 $delivery_fee  = 0.0;
    804                 $shipping_tax  = 0.0;
     803                $delivery_fee = 0.0;
     804                $shipping_tax = 0.0;
    805805                foreach ( $fee_lines as $line ) {
    806806                        $delivery_fee += (float) $line->get_total();
    … …  
    832832                // Mora delivery rate (Mora_Payment_Gateway::every_package_rate_or_
    833833                // false()), never on Local Pickup.
    834                 $cod_amount  = $is_mora_cod ? ( $order_total - $delivery_fee ) : 0.0;
    835                 $state_code  = $is_pickup ? null : ( $order->get_shipping_state() ?: $order->get_billing_state() );
     834                $cod_amount = $is_mora_cod ? ( $order_total - $delivery_fee ) : 0.0;
     835                $state_code = $is_pickup ? null : self::first_filled( $order->get_shipping_state(), $order->get_billing_state() );
    836836
    837837                return array(
    838838                        'merchant_order_id'  => (string) $order->get_id(),
    839839                        'site_url'           => home_url( '/' ),
    840                         'quote_id'           => $order->get_meta( '_mora_quote_id' ) ?: null,
     840                        'quote_id'           => self::filled_or_null( $order->get_meta( '_mora_quote_id' ) ),
    841841                        // Never null: OrderIn.placed_at is required and orders.placed_at is
    842842                        // NOT NULL, so a null here is a guaranteed 422, and a 422 is never
    … …  
    858858                        'fulfillment_type'   => $is_pickup ? 'pickup' : 'delivery',
    859859                        // Not sent: delivery_fee_collected, state_name. Both derived in mora-api.
    860                         'customer_name'      => trim( $order->get_shipping_first_name() . ' ' . $order->get_shipping_last_name() )
    861                                 ?: trim( $order->get_billing_first_name() . ' ' . $order->get_billing_last_name() ),
     860                        'customer_name'      => self::first_filled(
     861                                trim( $order->get_shipping_first_name() . ' ' . $order->get_shipping_last_name() ),
     862                                trim( $order->get_billing_first_name() . ' ' . $order->get_billing_last_name() )
     863                        ),
    862864                        'customer_phone'     => $order->get_billing_phone(),
    863                         'customer_phone_alt' => $order->get_meta( '_mora_phone_alt' ) ?: null,
     865                        'customer_phone_alt' => self::filled_or_null( $order->get_meta( '_mora_phone_alt' ) ),
    864866                        'customer_email'     => $order->get_billing_email(),
    865867                        // Every address/coordinate field below is null for a pickup order:
    … …  
    867869                        // delivery address to send at all (mora-api's own OrderIn requires
    868870                        // these only when fulfillment_type is "delivery").
    869                         'address_1'          => $is_pickup ? null : ( $order->get_shipping_address_1() ?: $order->get_billing_address_1() ),
    870                         'address_2'          => $is_pickup ? null : ( $order->get_shipping_address_2() ?: $order->get_billing_address_2() ),
    871                         'landmark'           => $is_pickup ? null : ( $order->get_meta( '_mora_landmark' ) ?: null ),
    872                         'city'               => $is_pickup ? null : ( $order->get_shipping_city() ?: $order->get_billing_city() ),
     871                        'address_1'          => $is_pickup ? null : self::first_filled( $order->get_shipping_address_1(), $order->get_billing_address_1() ),
     872                        'address_2'          => $is_pickup ? null : self::first_filled( $order->get_shipping_address_2(), $order->get_billing_address_2() ),
     873                        'landmark'           => $is_pickup ? null : self::filled_or_null( $order->get_meta( '_mora_landmark' ) ),
     874                        'city'               => $is_pickup ? null : self::first_filled( $order->get_shipping_city(), $order->get_billing_city() ),
    873875                        'state_code'         => $state_code,
    874                         'lga'                => $is_pickup ? null : ( $order->get_meta( '_mora_lga' ) ?: null ),
    875                         'pinned_address'     => $is_pickup ? null : ( $order->get_meta( '_mora_pinned_address' ) ?: null ),
     876                        'lga'                => $is_pickup ? null : self::filled_or_null( $order->get_meta( '_mora_lga' ) ),
     877                        'pinned_address'     => $is_pickup ? null : self::filled_or_null( $order->get_meta( '_mora_pinned_address' ) ),
    876878                        'lat'                => ( ! $is_pickup && '' !== $order->get_meta( '_mora_lat' ) ) ? (float) $order->get_meta( '_mora_lat' ) : null,
    877879                        'lon'                => ( ! $is_pickup && '' !== $order->get_meta( '_mora_lon' ) ) ? (float) $order->get_meta( '_mora_lon' ) : null,
    … …  
    887889                                array_values( $order->get_items() )
    888890                        ),
    889                         'notes'              => $order->get_customer_note() ?: null,
    890                 );
     891                        'notes'              => self::filled_or_null( $order->get_customer_note() ),
     892                );
     893        }
     894
     895        /**
     896         * The preferred value when it is truthy, otherwise the fallback. Same
     897         * result as PHP's short ternary ( $preferred ?: $fallback ), which the
     898         * coding standard disallows; both arguments are side-effect-free getters.
     899         *
     900         * @param mixed $preferred Value to use when truthy (e.g. a shipping field).
     901         * @param mixed $fallback  Value to use otherwise (e.g. the billing field).
     902         * @return mixed
     903         */
     904        private static function first_filled( $preferred, $fallback ) {
     905                return $preferred ? $preferred : $fallback;
     906        }
     907
     908        /**
     909         * The value when it is truthy, otherwise null (an empty meta string is
     910         * sent to mora-api as null, never as "").
     911         *
     912         * @param mixed $value Value to check.
     913         * @return mixed|null
     914         */
     915        private static function filled_or_null( $value ) {
     916                return $value ? $value : null;
    891917        }
    892918
  • mora-delivery-for-woocommerce/trunk/includes/class-mora-payment-gateway.php

    r3746963 r3749063  
    9494        public function __construct() {
    9595                $this->id                 = 'mora_pay_on_delivery';
    96                 $this->icon                = '';
    97                 $this->has_fields          = false;
    98                 $this->method_title        = __( 'Mora Goods on Delivery', 'mora-delivery-for-woocommerce' );
    99                 $this->method_description  = __( 'Charges only the order\'s delivery/shipping fee at checkout through the merchant\'s own Paystack account; any goods balance is collected in cash on delivery. Available for any order regardless of shipping method, as long as this gateway is enabled and a delivery fee is present; optionally restricted to Lagos-only destinations below.', 'mora-delivery-for-woocommerce' );
     96                $this->icon               = '';
     97                $this->has_fields         = false;
     98                $this->method_title       = __( 'Mora Goods on Delivery', 'mora-delivery-for-woocommerce' );
     99                $this->method_description = __( 'Charges only the order\'s delivery/shipping fee at checkout through the merchant\'s own Paystack account; any goods balance is collected in cash on delivery. Available for any order regardless of shipping method, as long as this gateway is enabled and a delivery fee is present; optionally restricted to Lagos-only destinations below.', 'mora-delivery-for-woocommerce' );
    100100                // Deliberately no 'refunds': a Paystack refund here is a manual action in
    101101                // the merchant's own PSP dashboard (README), not something this plugin
    … …  
    108108                $this->title              = $this->get_option( 'title' );
    109109                $this->description        = $this->get_option( 'description' );
    110                 $this->enabled             = $this->get_option( 'enabled', 'no' );
     110                $this->enabled            = $this->get_option( 'enabled', 'no' );
    111111                $this->balance_due_status = $this->get_option( 'balance_due_status', 'on-hold' );
    112112
    … …  
    125125        public function init_form_fields() {
    126126                $this->form_fields = array(
    127                         'enabled'                            => array(
     127                        'enabled'                           => array(
    128128                                'title'   => __( 'Enable', 'mora-delivery-for-woocommerce' ),
    129129                                'type'    => 'checkbox',
    … …  
    131131                                'default' => 'no',
    132132                        ),
    133                         'title'                               => array(
     133                        'title'                             => array(
    134134                                'title'       => __( 'Title', 'mora-delivery-for-woocommerce' ),
    135135                                'type'        => 'text',
    … …  
    138138                                'desc_tip'    => true,
    139139                        ),
    140                         'description'                        => array(
     140                        'description'                       => array(
    141141                                'title'   => __( 'Description', 'mora-delivery-for-woocommerce' ),
    142142                                'type'    => 'textarea',
    143143                                'default' => __( 'Pay the delivery fee now by card, bank transfer, or USSD. Any goods balance is paid to the rider on delivery.', 'mora-delivery-for-woocommerce' ),
    144144                        ),
    145                         'restrict_to_lagos'                  => array(
     145                        'restrict_to_lagos'                 => array(
    146146                                'title'       => __( 'Restrict to Lagos', 'mora-delivery-for-woocommerce' ),
    147147                                'type'        => 'checkbox',
    … …  
    150150                                'description' => __( 'Off by default: this gateway otherwise applies to any order regardless of shipping method or destination. Turn this on if the store delivers with Mora nationwide but only wants this fee-now/collect-on-delivery flow offered within Lagos; an interstate order with Mora chosen still delivers, it just pays in full upfront through another payment method.', 'mora-delivery-for-woocommerce' ),
    151151                        ),
    152                         'balance_due_status'                 => array(
     152                        'balance_due_status'                => array(
    153153                                'title'       => __( 'Order status while a goods balance is due', 'mora-delivery-for-woocommerce' ),
    154154                                'type'        => 'select',
    … …  
    160160                                'description' => __( 'Used only while a goods balance remains after the delivery fee is collected; an order with nothing left owing always goes straight to Processing regardless of this setting. Choose Processing if this store\'s fulfilment/warehouse tooling triggers off that status rather than On hold.', 'mora-delivery-for-woocommerce' ),
    161161                        ),
    162                         'hold_minutes'                        => array(
     162                        'hold_minutes'                      => array(
    163163                                'title'             => __( 'Hold unpaid bank transfer and USSD orders for', 'mora-delivery-for-woocommerce' ),
    164164                                'type'              => 'number',
    … …  
    174174                                // no longer polled. Previously enforced nowhere, client or
    175175                                // server side.
    176                                 'custom_attributes' => array( 'min' => '1', 'max' => (string) self::MAX_POLL_MINUTES ),
     176                                'custom_attributes' => array(
     177                                        'min' => '1',
     178                                        'max' => (string) self::MAX_POLL_MINUTES,
     179                                ),
    177180                                'description'       => __( 'Minutes to hold an order open, and prevent WooCommerce\'s own unpaid-order sweeper from cancelling it, while waiting for a bank transfer or USSD payment to verify. After this, Mora marks the order failed itself.', 'mora-delivery-for-woocommerce' ),
    178181                                'default'           => '180',
    179182                                'desc_tip'          => true,
    180183                        ),
    181                         'psp_driver'                          => array(
     184                        'psp_driver'                        => array(
    182185                                'title'   => __( 'Payment provider', 'mora-delivery-for-woocommerce' ),
    183186                                'type'    => 'select',
    … …  
    187190                                'default' => 'paystack',
    188191                        ),
    189                         'paystack_override_heading'          => array(
     192                        'paystack_override_heading'         => array(
    190193                                'title'       => __( 'Paystack keys (optional)', 'mora-delivery-for-woocommerce' ),
    191194                                'type'        => 'title',
    192195                                'description' => __( 'Leave these blank to use the keys already configured under WooCommerce > Settings > Payments > Paystack. Only fill these in if this store does not have that plugin installed.', 'mora-delivery-for-woocommerce' ),
    193196                        ),
    194                         'paystack_override_testmode'         => array(
     197                        'paystack_override_testmode'        => array(
    195198                                'title'   => __( 'Test mode', 'mora-delivery-for-woocommerce' ),
    196199                                'type'    => 'checkbox',
    … …  
    198201                                'default' => 'no',
    199202                        ),
    200                         'paystack_override_test_secret_key'  => array(
     203                        'paystack_override_test_secret_key' => array(
    201204                                'title'   => __( 'Test secret key', 'mora-delivery-for-woocommerce' ),
    202205                                'type'    => 'password',
    203206                                'default' => '',
    204207                        ),
    205                         'paystack_override_live_secret_key'  => array(
     208                        'paystack_override_live_secret_key' => array(
    206209                                'title'   => __( 'Live secret key', 'mora-delivery-for-woocommerce' ),
    207210                                'type'    => 'password',
    … …  
    323326                        return false;
    324327                }
    325                 $state = WC()->customer->get_shipping_state() ?: WC()->customer->get_billing_state();
     328                $state = WC()->customer->get_shipping_state();
     329                if ( ! $state ) {
     330                        $state = WC()->customer->get_billing_state();
     331                }
    326332                return self::LAGOS_STATE_CODE === $state;
    327333        }
    … …  
    399405                                sprintf(
    400406                                        'Mora: %s initialize() failed for order #%d: %s',
    401                                         $driver->id(), $order->get_id(), isset( $result['error'] ) ? $result['error'] : 'unknown error'
     407                                        $driver->id(),
     408                                        $order->get_id(),
     409                                        isset( $result['error'] ) ? $result['error'] : 'unknown error'
    402410                                ),
    403411                                array( 'source' => 'mora' )
    … …  
    418426                $order->save();
    419427
    420                 $order->add_order_note( sprintf(
    421                         'Mora: delivery-fee payment of %s started via %s (reference %s)%s.',
    422                         wc_price( $amount ), $driver->label(), $reference,
    423                         $driver->is_testmode() ? ' [TEST MODE]' : ''
    424                 ) );
     428                $order->add_order_note(
     429                        sprintf(
     430                                'Mora: delivery-fee payment of %s started via %s (reference %s)%s.',
     431                                wc_price( $amount ),
     432                                $driver->label(),
     433                                $reference,
     434                                $driver->is_testmode() ? ' [TEST MODE]' : ''
     435                        )
     436                );
    425437
    426438                $this->schedule_poll( $order->get_id(), $reference, 0, 0 );
    … …  
    589601                                sprintf(
    590602                                        'Mora: gave up polling reference %s for order #%d after %d minutes, still unpaid.',
    591                                         $reference, $order_id, $elapsed_minutes
     603                                        $reference,
     604                                        $order_id,
     605                                        $elapsed_minutes
    592606                                ),
    593607                                array( 'source' => 'mora' )
    … …  
    649663                                sprintf(
    650664                                        'Mora: verified payment amount/currency mismatch for order #%d, reference %s: expected %s %.2f, got %s %.2f. Not completing.',
    651                                         $order->get_id(), $reference, $expected_currency, $expected_amount,
    652                                         isset( $result['currency'] ) ? $result['currency'] : '?', $result['amount_major']
     665                                        $order->get_id(),
     666                                        $reference,
     667                                        $expected_currency,
     668                                        $expected_amount,
     669                                        isset( $result['currency'] ) ? $result['currency'] : '?',
     670                                        $result['amount_major']
    653671                                ),
    654672                                array( 'source' => 'mora' )
    … …  
    664682                        $order->update_meta_data( '_mora_psp_verified_at', current_time( 'mysql', true ) );
    665683                        $order->save();
    666                         $order->add_order_note( sprintf(
    667                                 'Mora: payment of %s verified against reference %s, but this order is already %s. The customer has paid and needs a refund or a new order. Not resurrected automatically.',
    668                                 wc_price( $result['amount_major'] ), $reference, $order->get_status()
    669                         ) );
     684                        $order->add_order_note(
     685                                sprintf(
     686                                        'Mora: payment of %s verified against reference %s, but this order is already %s. The customer has paid and needs a refund or a new order. Not resurrected automatically.',
     687                                        wc_price( $result['amount_major'] ),
     688                                        $reference,
     689                                        $order->get_status()
     690                                )
     691                        );
    670692                        wc_get_logger()->error(
    671693                                sprintf(
    672694                                        'Mora: verified payment against a cancelled/refunded/failed order #%d, reference %s.',
    673                                         $order->get_id(), $reference
     695                                        $order->get_id(),
     696                                        $reference
    674697                                ),
    675698                                array( 'source' => 'mora' )
    … …  
    701724                // participates in, so noting "fee collected" ahead of a status change
    702725                // that might still fail would describe something that didn't happen.
    703                 $order->add_order_note( sprintf(
    704                         'Mora: delivery-fee payment verified via %s (reference %s). Collected %s; %s owed on delivery.',
    705                         $driver->label(), $reference, wc_price( $result['amount_major'] ),
    706                         $goods_balance > 0 ? wc_price( $goods_balance ) : __( 'nothing', 'mora-delivery-for-woocommerce' )
    707                 ) );
     726                $order->add_order_note(
     727                        sprintf(
     728                                'Mora: delivery-fee payment verified via %s (reference %s). Collected %s; %s owed on delivery.',
     729                                $driver->label(),
     730                                $reference,
     731                                wc_price( $result['amount_major'] ),
     732                                $goods_balance > 0 ? wc_price( $goods_balance ) : __( 'nothing', 'mora-delivery-for-woocommerce' )
     733                        )
     734                );
    708735        }
    709736
    … …  
    733760                        return;
    734761                }
    735                 echo '<p>' . esc_html( sprintf(
     762                echo '<p>' . esc_html(
     763                        sprintf(
    736764                        /* translators: %s: goods balance still owed, formatted as a price. */
    737                         __( 'Your delivery fee has been paid. %s is still owed for the goods, payable to the rider on delivery.', 'mora-delivery-for-woocommerce' ),
    738                         wp_strip_all_tags( wc_price( (float) $balance ) )
    739                 ) ) . '</p>';
     765                                __( 'Your delivery fee has been paid. %s is still owed for the goods, payable to the rider on delivery.', 'mora-delivery-for-woocommerce' ),
     766                                wp_strip_all_tags( wc_price( (float) $balance ) )
     767                        )
     768                ) . '</p>';
    740769        }
    741770
    … …  
    759788                        if ( isset( $_GET[ self::COD_DISABLED_FLAG ] ) && '1' === sanitize_text_field( wp_unslash( $_GET[ self::COD_DISABLED_FLAG ] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification
    760789                                echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__(
    761                                         'WooCommerce\'s stock Cash on Delivery gateway has been disabled.', 'mora-delivery-for-woocommerce'
     790                                        'WooCommerce\'s stock Cash on Delivery gateway has been disabled.',
     791                                        'mora-delivery-for-woocommerce'
    762792                                ) . '</p></div>';
    763793                        } elseif ( $this->is_cod_enabled() ) {
  • mora-delivery-for-woocommerce/trunk/includes/class-mora-paystack-driver.php

    r3746963 r3749063  
    7777                if ( $has_override ) {
    7878                        $key = $this->override_testmode ? $this->override_test_secret_key : $this->override_live_secret_key;
    79                         return array( 'key' => $key, 'testmode' => $this->override_testmode );
    80                 }
    81 
    82                 $paystack_settings = get_option( 'woocommerce_paystack_settings', array() );
     79                        return array(
     80                                'key'      => $key,
     81                                'testmode' => $this->override_testmode,
     82                        );
     83                }
     84
     85                $paystack_settings  = get_option( 'woocommerce_paystack_settings', array() );
    8386                $inherited_testmode = isset( $paystack_settings['testmode'] ) && 'yes' === $paystack_settings['testmode'];
    84                 $key = $inherited_testmode
     87                $key                = $inherited_testmode
    8588                        ? ( isset( $paystack_settings['test_secret_key'] ) ? $paystack_settings['test_secret_key'] : '' )
    8689                        : ( isset( $paystack_settings['live_secret_key'] ) ? $paystack_settings['live_secret_key'] : '' );
    8790
    88                 return array( 'key' => $key, 'testmode' => $inherited_testmode );
     91                return array(
     92                        'key'      => $key,
     93                        'testmode' => $inherited_testmode,
     94                );
    8995        }
    9096
    … …  
    9298                $keys = $this->resolve_keys();
    9399                if ( '' === trim( $keys['key'] ) ) {
    94                         return array( 'ok' => false, 'error' => 'Paystack secret key is not configured.' );
     100                        return array(
     101                                'ok'    => false,
     102                                'error' => 'Paystack secret key is not configured.',
     103                        );
    95104                }
    96105
    … …  
    119128
    120129                if ( is_wp_error( $response ) ) {
    121                         return array( 'ok' => false, 'error' => $response->get_error_message() );
     130                        return array(
     131                                'ok'    => false,
     132                                'error' => $response->get_error_message(),
     133                        );
    122134                }
    123135
    … …  
    126138
    127139                if ( 200 !== $status || empty( $data['status'] ) || empty( $data['data']['authorization_url'] ) ) {
    128                         return array( 'ok' => false, 'error' => $this->error_message( $data, $status ) );
    129                 }
    130 
    131                 return array( 'ok' => true, 'redirect_url' => $data['data']['authorization_url'] );
     140                        return array(
     141                                'ok'    => false,
     142                                'error' => $this->error_message( $data, $status ),
     143                        );
     144                }
     145
     146                return array(
     147                        'ok'           => true,
     148                        'redirect_url' => $data['data']['authorization_url'],
     149                );
    132150        }
    133151
    … …  
    135153                $keys = $this->resolve_keys();
    136154                if ( '' === trim( $keys['key'] ) ) {
    137                         return array( 'ok' => false, 'error' => 'Paystack secret key is not configured.' );
     155                        return array(
     156                                'ok'    => false,
     157                                'error' => 'Paystack secret key is not configured.',
     158                        );
    138159                }
    139160
    … …  
    147168
    148169                if ( is_wp_error( $response ) ) {
    149                         return array( 'ok' => false, 'error' => $response->get_error_message() );
     170                        return array(
     171                                'ok'    => false,
     172                                'error' => $response->get_error_message(),
     173                        );
    150174                }
    151175
    … …  
    154178
    155179                if ( 200 !== $status || empty( $data['status'] ) || ! isset( $data['data'] ) ) {
    156                         return array( 'ok' => false, 'error' => $this->error_message( $data, $status ) );
     180                        return array(
     181                                'ok'    => false,
     182                                'error' => $this->error_message( $data, $status ),
     183                        );
    157184                }
    158185
  • mora-delivery-for-woocommerce/trunk/includes/class-mora-sentry-reporter.php

    r3746963 r3749063  
    6464        // elsewhere in this plugin (order meta, checkout fields).
    6565        const PII_KEYS = array(
    66                 'customer_name', 'customer_email', 'customer_phone', 'phone', 'email',
    67                 'address', 'address_1', 'address_2', 'billing_email', 'billing_phone',
    68                 'first_name', 'last_name',
     66                'customer_name',
     67                'customer_email',
     68                'customer_phone',
     69                'phone',
     70                'email',
     71                'address',
     72                'address_1',
     73                'address_2',
     74                'billing_email',
     75                'billing_phone',
     76                'first_name',
     77                'last_name',
    6978        );
    7079
    … …  
    107116                        $parsed['store_url'],
    108117                        array(
    109                                 'headers'   => array(
     118                                'headers'  => array(
    110119                                        'Content-Type'  => 'application/json',
    111120                                        'X-Sentry-Auth' => self::auth_header( $parsed['public_key'] ),
    112121                                ),
    113                                 'body'      => wp_json_encode( $event ),
    114                                 'timeout'   => self::TIMEOUT_SECONDS,
    115                                 'blocking'  => false,
     122                                'body'     => wp_json_encode( $event ),
     123                                'timeout'  => self::TIMEOUT_SECONDS,
     124                                'blocking' => false,
    116125                        )
    117126                );
  • mora-delivery-for-woocommerce/trunk/includes/class-mora-shipping-method.php

    r3746963 r3749063  
    7373                $this->enabled = $this->get_option( 'enabled', 'yes' );
    7474
    75                 $credentials         = Mora_Admin_Settings::site_wide_credentials();
     75                $credentials        = Mora_Admin_Settings::site_wide_credentials();
    7676                $this->api_base_url = untrailingslashit( $credentials['api_base_url'] );
    77                 $this->api_key       = $credentials['api_key'];
     77                $this->api_key      = $credentials['api_key'];
    7878
    7979                add_action( 'woocommerce_update_options_shipping_' . $this->id, array( $this, 'process_admin_options' ) );
    … …  
    120120                $locator_on = Mora_Admin_Settings::is_places_locator_enabled();
    121121                $location   = $locator_on ? Mora_Checkout::get_pinned_location() : array();
    122                 $lat      = isset( $location['lat'] ) ? $location['lat'] : null;
    123                 $lon      = isset( $location['lon'] ) ? $location['lon'] : null;
     122                $lat        = isset( $location['lat'] ) ? $location['lat'] : null;
     123                $lon        = isset( $location['lon'] ) ? $location['lon'] : null;
    124124
    125125                $has_coordinates = ( null !== $lat && null !== $lon );
    … …  
    129129                // here either when there is a pin.
    130130                $address = $has_coordinates ? null : $this->customer_address_text( $package, $state_name );
    131                 $quote = Mora_Api_Client::get_quote( $this->api_base_url, $this->api_key, $state_name, $lat, $lon, $address );
     131                $quote   = Mora_Api_Client::get_quote( $this->api_base_url, $this->api_key, $state_name, $lat, $lon, $address );
    132132
    133133                // Whether the customer has already given us an address to work from,
  • mora-delivery-for-woocommerce/trunk/includes/class-mora-webhook-receiver.php

    r3746963 r3749063  
    7979                if ( '' === $secret ) {
    8080                        self::log_throttled( 'error', 'Mora: webhook received but no webhook secret is configured on this site.', 'no_secret' );
    81                         return new WP_REST_Response( array( 'ok' => false, 'error' => 'webhook not configured' ), 501 );
     81                        return new WP_REST_Response(
     82                                array(
     83                                        'ok'    => false,
     84                                        'error' => 'webhook not configured',
     85                                ),
     86                                501
     87                        );
    8288                }
    8389
    … …  
    8793                // number formatting). Same reasoning as the documented-but-unbuilt
    8894                // Paystack signature check elsewhere in this codebase.
    89                 $raw_body = $request->get_body();
     95                $raw_body           = $request->get_body();
    9096                $received_signature = (string) $request->get_header( 'x_mora_webhook_signature' );
    9197                if ( '' === $received_signature ) {
    92                         return new WP_REST_Response( array( 'ok' => false, 'error' => 'missing signature' ), 401 );
     98                        return new WP_REST_Response(
     99                                array(
     100                                        'ok'    => false,
     101                                        'error' => 'missing signature',
     102                                ),
     103                                401
     104                        );
    93105                }
    94106
    … …  
    96108                if ( ! hash_equals( $expected_signature, $received_signature ) ) {
    97109                        self::log_throttled( 'warning', 'Mora: webhook signature did not match.', 'bad_signature' );
    98                         return new WP_REST_Response( array( 'ok' => false, 'error' => 'invalid signature' ), 401 );
     110                        return new WP_REST_Response(
     111                                array(
     112                                        'ok'    => false,
     113                                        'error' => 'invalid signature',
     114                                ),
     115                                401
     116                        );
    99117                }
    100118
    101119                $payload = json_decode( $raw_body, true );
    102120                if ( ! is_array( $payload ) || empty( $payload['merchant_order_id'] ) || empty( $payload['status'] ) ) {
    103                         return new WP_REST_Response( array( 'ok' => false, 'error' => 'malformed payload' ), 400 );
     121                        return new WP_REST_Response(
     122                                array(
     123                                        'ok'    => false,
     124                                        'error' => 'malformed payload',
     125                                ),
     126                                400
     127                        );
    104128                }
    105129
    … …  
    113137                                array( 'source' => 'mora' )
    114138                        );
    115                         return new WP_REST_Response( array( 'ok' => false, 'error' => 'order not found' ), 404 );
     139                        return new WP_REST_Response(
     140                                array(
     141                                        'ok'    => false,
     142                                        'error' => 'order not found',
     143                                ),
     144                                404
     145                        );
    116146                }
    117147
    … …  
    130160                                array( 'source' => 'mora' )
    131161                        );
    132                         return new WP_REST_Response( array( 'ok' => false, 'error' => 'order_id mismatch' ), 409 );
     162                        return new WP_REST_Response(
     163                                array(
     164                                        'ok'    => false,
     165                                        'error' => 'order_id mismatch',
     166                                ),
     167                                409
     168                        );
    133169                }
    134170
  • mora-delivery-for-woocommerce/trunk/mora-delivery-for-woocommerce.php

    r3748164 r3749063  
    33 * Plugin Name: Mora Delivery for WooCommerce
    44 * Description: Live Mora delivery quotes at checkout, backed by mora-api's GET /v1/quote, a goods-on-delivery payment gateway that charges the delivery fee upfront, and pushes placed orders on to Mora.
    5  * Version: 0.7.78
     5 * Version: 0.7.79
    66 * Requires at least: 6.0
    77 * Requires PHP: 7.4
    … …  
    2121}
    2222
    23 define( 'MORA_WC_VERSION', '0.7.78' );
     23define( 'MORA_WC_VERSION', '0.7.79' );
    2424define( 'MORA_WC_PLUGIN_FILE', __FILE__ );
    2525define( 'MORA_WC_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
  • mora-delivery-for-woocommerce/trunk/readme.txt

    r3748164 r3749063  
    55Tested up to: 7.1
    66Requires PHP: 7.4
    7 Stable tag: 0.7.78
     7Stable tag: 0.7.79
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
    … …  
    3131This plugin connects to the following services to provide its features. None of them run unless the matching feature is actually configured and in use.
    3232
    33 **Mora Logistics API (mora-api)**: Mora's own backend, used to price delivery quotes, process the delivery-fee payment gateway's PSP verification, and push/sync placed orders. When a quote is requested, this plugin sends the destination state, and, where available, the customer's pinned coordinates or typed address, to `https://api.moralogistics.com`. When an order is placed, order, product, and customer contact details are sent there so Mora can fulfil the delivery. This is Mora's own first-party service, governed by the same merchant agreement a Mora Logistics merchant signs; see Mora Logistics' Terms of Service (https://moralogistics.com/terms) and Privacy Policy (https://moralogistics.com/privacy-policy).
     33**Mora Logistics API (mora-api)**: Mora's own backend, used to price delivery quotes and to push and sync placed orders. When a quote is requested, this plugin sends the destination state, and, where available, the customer's pinned coordinates or typed address, to the Mora API address set in the plugin's settings (Mora's production service is `https://api.moralogistics.com`). When an order is placed, order, product, and customer contact details are sent there so Mora can fulfil the delivery. This is Mora's own first-party service, governed by the same merchant agreement a Mora Logistics merchant signs; see Mora Logistics' Terms of Service (https://moralogistics.com/terms) and Privacy Policy (https://moralogistics.com/privacy-policy).
    3434
    3535**Paystack**: used only by this plugin's own payment gateway (optional, merchant-enabled), through the merchant's own Paystack account. The order's delivery-fee amount, currency, and a payment reference are sent to Paystack to process the charge (sent to `https://api.paystack.co` when the customer places an order with this gateway, and again to verify the payment); no card details pass through this plugin or site, Paystack's own hosted page handles that directly. See Paystack's Terms of Use (https://paystack.com/terms) and Merchant Privacy Policy (https://paystack.com/privacy/merchant).
    … …  
    7171
    7272== Changelog ==
     73
     74= 0.7.79 =
     75* Fixed: hub-pickup orders from the block checkout (WooCommerce Local Pickup) were never sent to Mora. They are now, like classic-checkout pickup orders.
     76* Docs: External Services no longer says payments are verified through Mora's API (they are verified directly with Paystack), and names the configured API address.
    7377
    7478= 0.7.78 =
  • mora-delivery-for-woocommerce/trunk/uninstall.php

    r3746963 r3749063  
    2121
    2222// This plugin's own site-wide options.
    23 $options = array(
     23$mora_wc_options = array(
    2424        'mora_wc_places_api_key',
    2525        'mora_wc_reorder_core_fields',
    … …  
    3838        'woocommerce_mora_settings',
    3939);
    40 foreach ( $options as $option ) {
    41         delete_option( $option );
     40foreach ( $mora_wc_options as $mora_wc_option ) {
     41        delete_option( $mora_wc_option );
    4242}
    4343
Note: See TracChangeset for help on using the changeset viewer.