Plugin Directory

Changeset 3768791


Ignore:
Timestamp:
10/27/2026 02:27:24 PM (4 days ago)
Author:
mintagepay
Message:

Release 1.18.0: Connect to Mintage Pay (no key to copy), Get started box, automatic-update setting, WooCommerce Payments page support, app icon fix.

Location:
mintage-pay
Files:
4 added
12 edited
1 copied

Legend:

Unmodified
Added
Removed
  • mintage-pay/tags/1.18.0/includes/class-mintagepay-admin.php

    r3768248 r3768791  
    166166         * Webhook connection
    167167         * ------------------------------------------------------------------ */
     168
     169        /**
     170         * After the API key was saved (the settings form, or "Connect to Mintage Pay", 1.18.0): check the new key with
     171         * GET /v1/me, remember it as verified, and connect the webhook (deleting the old key's endpoint). The same steps for
     172         * both, so a connected store is set up exactly like one where the key was pasted.
     173         *
     174         * @param string $old_key      The key saved before.
     175         * @param string $old_base     The API base the old key used (Mintage_Pay::api_base_for()).
     176         * @param string $new_key      The key saved now ('' = removed).
     177         * @param string $base_setting The saved "API base URL" setting ('' = automatic).
     178         * @return string 'removed', 'unchanged', 'verified' (the webhook was already connected), 'connected', or 'failed'.
     179         */
     180        public static function activate_key( $old_key, $old_base, $new_key, $base_setting ) {
     181                $old_key  = (string) $old_key;
     182                $new_key  = (string) $new_key;
     183                $new_base = Mintage_Pay::api_base_for( $new_key, (string) $base_setting );
     184                delete_transient( Mintage_Pay::RAILS_TRANSIENT );
     185                $old_api = '' !== $old_key ? Mintage_Pay::api( $old_key, $old_base ) : null;
     186
     187                if ( '' === $new_key ) {
     188                        // Key removed: disable the old endpoint and forget the secret.
     189                        $hook = Mintage_Pay::get_webhook();
     190                        if ( $hook && $old_api && isset( $hook['fingerprint'] ) && Mintage_Pay::fingerprint( $old_base, $old_key ) === $hook['fingerprint'] ) {
     191                                $old_api->delete_webhook( $hook['id'] );
     192                        }
     193                        Mintage_Pay::clear_webhook();
     194                        Mintage_Pay::clear_key_verified();
     195                        return 'removed';
     196                }
     197
     198                $credentials_changed = ( $new_key !== $old_key || $new_base !== $old_base );
     199                $hook                = Mintage_Pay::get_webhook();
     200                $fingerprint         = Mintage_Pay::fingerprint( $new_base, $new_key );
     201                $hook_ok             = $hook && ! empty( $hook['secret'] ) && isset( $hook['fingerprint'] ) && $hook['fingerprint'] === $fingerprint;
     202
     203                if ( ! $credentials_changed && $hook_ok && Mintage_Pay::is_key_verified() ) {
     204                        return 'unchanged';
     205                }
     206
     207                $api = Mintage_Pay::api( $new_key, $new_base );
     208                $me  = $api->get_me();
     209                if ( is_wp_error( $me ) ) {
     210                        // A new key is unverified by definition (the stored flag holds another key's fingerprint).
     211                        self::handle_me_error( $me );
     212                        Mintage_Pay::add_notice(
     213                                'error',
     214                                sprintf(
     215                                        /* translators: %s: error message */
     216                                        __( 'Mintage Pay: the API key could not be verified (%s). The payment method stays hidden at checkout until the key is verified. Check the key and save again.', 'mintage-pay' ),
     217                                        self::me_error_text( $me, $new_key, (string) $base_setting )
     218                                )
     219                        );
     220                        return 'failed';
     221                }
     222
     223                $merchant_id = self::merchant_id( $me );
     224                $name        = self::merchant_name( $me );
     225                Mintage_Pay::mark_key_verified( $api->fingerprint(), $merchant_id, $name );
     226                Mintage_Pay::cache_rails( $me );
     227
     228                if ( $hook_ok ) {
     229                        Mintage_Pay::add_notice( 'success', self::connected_message( $name, $new_key ) );
     230                        return 'verified';
     231                }
     232
     233                $previous_api = null;
     234                if ( $hook && isset( $hook['fingerprint'] ) ) {
     235                        if ( $old_api && Mintage_Pay::fingerprint( $old_base, $old_key ) === $hook['fingerprint'] ) {
     236                                $previous_api = $old_api;
     237                        } elseif ( $hook['fingerprint'] === $fingerprint ) {
     238                                $previous_api = $api;
     239                        }
     240                }
     241
     242                $result = self::connect_webhook( $api, $previous_api, $merchant_id );
     243                if ( true === $result ) {
     244                        Mintage_Pay::add_notice( 'success', self::connected_message( $name, $new_key ) . ' ' . __( 'Webhook connected.', 'mintage-pay' ) );
     245                        return 'connected';
     246                }
     247                return 'failed';
     248        }
     249
     250        /**
     251         * "Mintage Pay: API key verified for X (live)."
     252         *
     253         * @param string $name    Merchant name.
     254         * @param string $api_key The key (test or live).
     255         * @return string
     256         */
     257        public static function connected_message( $name, $api_key ) {
     258                $mode = Mintage_Pay::is_test_key( (string) $api_key )
     259                        ? __( 'TEST MODE: sandbox, test money only', 'mintage-pay' )
     260                        : __( 'live', 'mintage-pay' );
     261                return '' !== $name
     262                        /* translators: 1: merchant business name, 2: "live" or "TEST MODE: ..." */
     263                        ? sprintf( __( 'Mintage Pay: API key verified for %1$s (%2$s).', 'mintage-pay' ), $name, $mode )
     264                        /* translators: %s: "live" or "TEST MODE: ..." */
     265                        : sprintf( __( 'Mintage Pay: API key verified (%s).', 'mintage-pay' ), $mode );
     266        }
    168267
    169268        /**
  • mintage-pay/tags/1.18.0/includes/class-mintagepay-blocks.php

    r3768248 r3768791  
    105105
    106106        /**
    107          * The apps' marks for this method's own row (WC_Gateway_MintagePay::get_icon() for the classic checkout): Cash App,
    108          * plus Apple Pay / Google Pay where Mintage Pay offers the card route for the cart's total. None while the two
    109          * choices are inside the method (the marks are on the choices then) or when it is the only rail.
     107         * The apps' marks for this method's own row (WC_Gateway_MintagePay::get_icon() for the classic checkout), by the one
     108         * rule in Mintage_Pay_Two_Rail::row_marks(): only what Pay by app offers this store for the cart's total, none while
     109         * the two choices are inside the method (the marks are on the choices then).
    110110         *
    111111         * @return array[]
    … …  
    117117                $currency = function_exists( 'get_woocommerce_currency' ) ? (string) get_woocommerce_currency() : 'USD';
    118118                $total    = $this->cart_total();
    119                 $apps     = Mintage_Pay::split_on()
    120                         || ( Mintage_Pay::crypto_on() && ! ( $total > 0 && Mintage_Pay_Two_Rail::offered( $total, $currency ) ) );
    121                 if ( ! $apps ) {
    122                         return array();
    123                 }
    124                 $wallets = $total > 0 ? Mintage_Pay_Two_Rail::card_status( $total, $currency )['available'] : true;
    125                 return Mintage_Pay_Two_Rail::mark_images( 'app', $wallets );
     119                // The same rule as the classic checkout (Mintage_Pay_Two_Rail::row_marks()); no cart (the editor): $total is 0.
     120                return Mintage_Pay_Two_Rail::row_mark_images( $total, $currency, $total <= 0 );
    126121        }
    127122
  • mintage-pay/tags/1.18.0/includes/class-mintagepay-two-rail.php

    r3768248 r3768791  
    223223         */
    224224        public static function card_status( $total, $currency ) {
    225                 $none     = self::card_from_options( null );
     225                return self::status( $total, $currency )['card'];
     226        }
     227
     228        /**
     229         * Read `options.cash_app` and `options.wallet` of a GET /v1/payment-options response (1.18.0): whether Pay by app
     230         * offers Cash App, and crypto, for this store and total. Pure. A server that does not send them offers neither.
     231         *
     232         * @param mixed $options Response.
     233         * @return array { cash_app: bool, wallet: bool }
     234         */
     235        public static function apps_from_options( $options ) {
     236                $o = is_array( $options ) && isset( $options['options'] ) && is_array( $options['options'] ) ? $options['options'] : array();
     237                return array(
     238                        'cash_app' => isset( $o['cash_app'] ) && true === $o['cash_app'],
     239                        'wallet'   => isset( $o['wallet'] ) && true === $o['wallet'],
     240                );
     241        }
     242
     243        /**
     244         * The card route and the apps for an amount, from one GET /v1/payment-options (card_status() caching: 10 minutes per
     245         * amount, 2 after a failure). Unknown (no verified key, the API failed): no card, no apps.
     246         *
     247         * @param float|string $total    Order total in the store currency.
     248         * @param string       $currency ISO 4217.
     249         * @return array { card: card_from_options(), apps: apps_from_options() }
     250         */
     251        public static function status( $total, $currency ) {
     252                $none     = array(
     253                        'card' => self::card_from_options( null ),
     254                        'apps' => self::apps_from_options( null ),
     255                );
    226256                $currency = strtoupper( (string) $currency );
    227257                if ( ! class_exists( 'Mintage_Pay_Webhook' ) || ! Mintage_Pay_Webhook::currency_supported( $currency ) || ! Mintage_Pay::is_key_verified() ) {
    … …  
    238268                $key    = self::TRANSIENT . md5( $api->fingerprint() . '|' . $currency . '|' . $minor );
    239269                $cached = get_transient( $key );
    240                 if ( is_array( $cached ) && isset( $cached['available'] ) ) {
    241                         return self::card_from_options( array( 'card' => $cached ) );
     270                // A value cached by 1.17.0 has no 'apps': asked again.
     271                if ( is_array( $cached ) && isset( $cached['available'] ) && isset( $cached['apps'] ) && is_array( $cached['apps'] ) ) {
     272                        return array(
     273                                'card' => self::card_from_options( array( 'card' => $cached ) ),
     274                                'apps' => self::apps_from_options( array( 'options' => $cached['apps'] ) ),
     275                        );
    242276                }
    243277                $options = $api->payment_options( Mintage_Pay_Webhook::format_minor( $minor, $currency ), $currency, 5 );
    244278                $card    = self::card_from_options( is_wp_error( $options ) ? null : $options );
    245                 set_transient( $key, $card, is_wp_error( $options ) ? 2 * MINUTE_IN_SECONDS : 10 * MINUTE_IN_SECONDS );
     279                $apps    = self::apps_from_options( is_wp_error( $options ) ? null : $options );
     280                set_transient( $key, $card + array( 'apps' => $apps ), is_wp_error( $options ) ? 2 * MINUTE_IN_SECONDS : 10 * MINUTE_IN_SECONDS );
    246281                if ( is_wp_error( $options ) ) {
    247282                        Mintage_Pay::log( 'payment options could not be read (the card choice stays hidden for now): ' . $options->get_error_message(), 'warning' );
    248283                }
    249                 return $card;
     284                return array(
     285                        'card' => $card,
     286                        'apps' => $apps,
     287                );
     288        }
     289
     290        /**
     291         * The marks on the main method's own row at checkout (classic get_icon() and the block checkout's data use this
     292         * one rule, 1.18.0). Pure.
     293         *  - The two choices are inside the method: none (the marks are on the choices).
     294         *  - No cart (admin, the block editor): the apps' marks where the method is the apps (split, or crypto its own
     295         *    method), none otherwise, as before 1.18.0.
     296         *  - Otherwise only where Pay by app offers Cash App for this store (options.cash_app): Cash App, then Apple Pay and
     297         *    Google Pay where the card route is offered, then USDC and USDT where crypto is inside Pay by app (crypto not its
     298         *    own method) and offered (options.wallet). A store without Cash App, or a failed read, shows none.
     299         *
     300         * @param bool       $split   "Pay by card" is its own method (Mintage_Pay::split_on()).
     301         * @param bool       $crypto  "Pay with crypto" is its own method (Mintage_Pay::crypto_on()).
     302         * @param bool       $offered The two choices are offered inside the method for this total.
     303         * @param array|null $status  status() for the cart's total, or null when there is no cart.
     304         * @return array[] marks() entries.
     305         */
     306        public static function row_marks( $split, $crypto, $offered, $status ) {
     307                if ( ! $split && $offered ) {
     308                        return array();
     309                }
     310                if ( null === $status ) {
     311                        return ( $split || $crypto ) ? self::marks( 'app', true ) : array();
     312                }
     313                if ( empty( $status['apps']['cash_app'] ) ) {
     314                        return array();
     315                }
     316                return self::marks( 'app', ! empty( $status['card']['available'] ), ! $crypto && ! empty( $status['apps']['wallet'] ) );
     317        }
     318
     319        /**
     320         * row_marks() for this store and cart, as image addresses.
     321         *
     322         * @param float|string $total    Cart or order total.
     323         * @param string       $currency ISO 4217.
     324         * @param bool         $no_cart  There is no cart (admin, the block editor).
     325         * @return array[]
     326         */
     327        public static function row_mark_images( $total, $currency, $no_cart ) {
     328                $status  = $no_cart ? null : self::status( $total, $currency );
     329                $offered = ! $no_cart && self::offered( $total, $currency );
     330                return self::images( self::row_marks( Mintage_Pay::split_on(), Mintage_Pay::crypto_on(), $offered, $status ) );
    250331        }
    251332
    … …  
    329410         * @param string $choice  'card', 'app' or 'crypto'.
    330411         * @param bool   $wallets Apple Pay and Google Pay are offered on Pay by app.
     412         * @param bool   $coins   USDC and USDT after the apps (Pay by app with crypto inside it, row_marks()).
    331413         * @return array[]
    332414         */
    333         public static function marks( $choice, $wallets = true ) {
     415        public static function marks( $choice, $wallets = true, $coins = false ) {
    334416                if ( 'card' === $choice ) {
    335417                        return array(
    … …  
    350432                        $out[] = array( 'google-pay', 'Google Pay', 45 );
    351433                }
     434                if ( $coins ) {
     435                        $out[] = array( 'usdc', 'USDC', 38 );
     436                        $out[] = array( 'usdt', 'USDT', 38 );
     437                }
    352438                return $out;
    353439        }
    … …  
    361447         */
    362448        public static function mark_images( $choice, $wallets = true ) {
     449                return self::images( self::marks( $choice, $wallets ) );
     450        }
     451
     452        /**
     453         * marks() entries as addresses: file, label, width.
     454         *
     455         * @param array[] $marks marks() entries.
     456         * @return array[]
     457         */
     458        public static function images( array $marks ) {
    363459                $base = plugins_url( 'assets/img/marks/', MINTAGE_PAY_FILE );
    364460                $out  = array();
    365                 foreach ( self::marks( $choice, $wallets ) as $m ) {
     461                foreach ( $marks as $m ) {
    366462                        $out[] = array(
    367463                                'src'   => $base . $m[0] . '.svg?ver=' . MINTAGE_PAY_VERSION,
    … …  
    382478         */
    383479        public static function marks_html( $choice, $wallets = true ) {
     480                return self::images_html( self::mark_images( $choice, $wallets ) );
     481        }
     482
     483        /**
     484         * Image addresses (images()) as the payment method row's HTML.
     485         *
     486         * @param array[] $images images() entries.
     487         * @return string
     488         */
     489        public static function images_html( array $images ) {
    384490                $html = '';
    385                 foreach ( self::mark_images( $choice, $wallets ) as $m ) {
     491                foreach ( $images as $m ) {
    386492                        $html .= '<img class="mintagepay-method-mark" src="' . esc_url( $m['src'] ) . '" alt="' . esc_attr( $m['alt'] ) . '" title="' . esc_attr( $m['alt'] ) . '" width="' . (int) $m['width'] . '" height="24" decoding="async" />';
    387493                }
  • mintage-pay/tags/1.18.0/includes/class-wc-gateway-mintagepay.php

    r3768248 r3768791  
    2727                // claiming a refund was sent (WooCommerce would otherwise only offer "Refund manually").
    2828                $this->supports           = array_merge( Mintage_Pay_Subscriptions::supports(), array( 'refunds' ) );
     29                // The square Mintage Pay mark for WooCommerce's Payments page (it reads this property, over REST too). The
     30                // checkout row never shows it: get_icon() below does not call parent::get_icon().
     31                $this->icon               = plugins_url( 'assets/img/mintage-pay.svg', MINTAGE_PAY_FILE );
    2932
    3033                $this->init_form_fields();
    … …  
    8083        public function get_icon() {
    8184                $rail = $this->method_rail();
    82                 // With "Pay with crypto" as its own method (1.17.0) this method is the apps, unless it shows the two choices
    83                 // inside itself (their marks are on the choices then).
    84                 if ( '' === $rail && Mintage_Pay::crypto_on() && ! $this->two_rail_offered() ) {
    85                         $rail = 'app';
    86                 }
    87                 if ( '' === $rail ) {
    88                         return parent::get_icon();
    89                 }
    90                 return apply_filters( 'woocommerce_gateway_icon', Mintage_Pay_Two_Rail::marks_html( $rail, $this->wallets_offered() ), $this->id );
     85                if ( 'card' === $rail || 'crypto' === $rail ) {
     86                        return apply_filters( 'woocommerce_gateway_icon', Mintage_Pay_Two_Rail::marks_html( $rail, $this->wallets_offered() ), $this->id );
     87                }
     88                // This method (Pay by app): one rule with the block checkout (Mintage_Pay_Two_Rail::row_marks(), 1.18.0). Never
     89                // parent::get_icon(): $this->icon is set for the WooCommerce Payments page only, not for the checkout row.
     90                $no_cart  = is_admin() && ! wp_doing_ajax();
     91                $currency = function_exists( 'get_woocommerce_currency' ) ? (string) get_woocommerce_currency() : 'USD';
     92                $images   = Mintage_Pay_Two_Rail::row_mark_images( $no_cart ? 0 : $this->get_order_total(), $currency, $no_cart );
     93                return apply_filters( 'woocommerce_gateway_icon', Mintage_Pay_Two_Rail::images_html( $images ), $this->id );
    9194        }
    9295
    … …  
    306309                                'title'       => __( 'API key', 'mintage-pay' ),
    307310                                'type'        => 'password',
    308                                 'description' => __( 'Create one in the Mintage Pay dashboard under Developers > API keys. Saving a new key checks it and connects the webhook automatically.', 'mintage-pay' ),
     311                                'description' => __( 'Easiest: click "Connect to Mintage Pay" at the top of this page and the key is filled in for you. Or create one in the Mintage Pay dashboard under Developers > API keys and paste it here. Saving a new key checks it and connects the webhook automatically.', 'mintage-pay' ),
    309312                                'default'     => '',
    310313                        ),
    … …  
    366369                                'type'  => 'mintagepay_webhook',
    367370                        ),
     371                        'auto_update'        => array(
     372                                'title' => __( 'Plugin updates', 'mintage-pay' ),
     373                                'type'  => 'mintagepay_auto_update',
     374                        ),
    368375                        'debug'              => array(
    369376                                'title'       => __( 'Debug log', 'mintage-pay' ),
    … …  
    399406                        <td class="forminp">
    400407                                <?php if ( '' === (string) $this->get_option( 'api_key' ) ) : ?>
    401                                         <p><?php esc_html_e( 'Not connected. Save an API key first.', 'mintage-pay' ); ?></p>
     408                                        <p><?php esc_html_e( 'Not connected yet.', 'mintage-pay' ); ?></p>
     409                                        <p><?php $this->connect_buttons(); ?></p>
    402410                                <?php else : ?>
    403411                                        <?php if ( $hook && ! empty( $hook['secret'] ) && isset( $hook['fingerprint'] ) && $hook['fingerprint'] === $fingerprint ) : ?>
    … …  
    446454
    447455        /**
     456         * The two Connect buttons (live account, sandbox test account).
     457         */
     458        protected function connect_buttons() {
     459                ?>
     460                <a class="button button-primary" href="<?php echo esc_url( Mintage_Pay_Connect::start_url( 'live' ) ); ?>"><?php esc_html_e( 'Connect to Mintage Pay', 'mintage-pay' ); ?></a>
     461                <a class="button" href="<?php echo esc_url( Mintage_Pay_Connect::start_url( 'test' ) ); ?>"><?php esc_html_e( 'Connect a test (sandbox) account', 'mintage-pay' ); ?></a>
     462                <?php
     463        }
     464
     465        /**
     466         * Settings page: the "Get started" panel (no key yet) or the "Replace the key?" box (a Connect came back while a
     467         * working key is saved), then the settings. Only on this method's own page, not the card or crypto method's.
     468         */
     469        public function admin_options() {
     470                if ( Mintage_Pay::GATEWAY_ID === $this->id ) {
     471                        $pending = Mintage_Pay_Connect::pending();
     472                        if ( $pending ) {
     473                                $this->confirm_box( $pending );
     474                        } elseif ( '' === (string) $this->get_option( 'api_key' ) ) {
     475                                $this->get_started_panel();
     476                        }
     477                }
     478                parent::admin_options();
     479        }
     480
     481        /**
     482         * "Get started": connect, try the sandbox, apply, read the guide.
     483         */
     484        private function get_started_panel() {
     485                ?>
     486                <div class="notice notice-info inline" style="padding:12px 16px;margin:16px 0;">
     487                        <h3 style="margin-top:0;"><?php esc_html_e( 'Get started with Mintage Pay', 'mintage-pay' ); ?></h3>
     488                        <ol style="margin-left:1.5em;">
     489                                <li><?php esc_html_e( 'Have a Mintage Pay account? Click "Connect to Mintage Pay", sign in and approve this store. The API key and the webhook are set up for you and the payment method is switched on.', 'mintage-pay' ); ?></li>
     490                                <li>
     491                                        <?php
     492                                        printf(
     493                                                /* translators: %s: link to the Mintage Pay application form */
     494                                                esc_html__( 'No account yet? %s (a few minutes).', 'mintage-pay' ),
     495                                                '<a href="https://mintagepay.com/#apply" target="_blank" rel="noopener noreferrer">' . esc_html__( 'Apply for a Mintage Pay account', 'mintage-pay' ) . '</a>'
     496                                        );
     497                                        ?>
     498                                </li>
     499                                <li>
     500                                        <?php
     501                                        printf(
     502                                                /* translators: %s: link to the sandbox */
     503                                                esc_html__( 'Want to try it first? Make a free test account on the %s, then click "Connect a test (sandbox) account". Test orders use test money only.', 'mintage-pay' ),
     504                                                '<a href="https://sandbox.mintagepay.com/" target="_blank" rel="noopener noreferrer">' . esc_html__( 'Mintage Pay sandbox', 'mintage-pay' ) . '</a>'
     505                                        );
     506                                        ?>
     507                                </li>
     508                        </ol>
     509                        <p><?php $this->connect_buttons(); ?></p>
     510                        <p class="description">
     511                                <?php
     512                                printf(
     513                                        /* translators: %s: link to the setup guide */
     514                                        esc_html__( 'Step by step: %s.', 'mintage-pay' ),
     515                                        '<a href="https://mintagepay.com/docs/integrations/#woocommerce" target="_blank" rel="noopener noreferrer">' . esc_html__( 'the WooCommerce setup guide', 'mintage-pay' ) . '</a>'
     516                                );
     517                                ?>
     518                        </p>
     519                </div>
     520                <?php
     521        }
     522
     523        /**
     524         * A Connect came back while a working key is saved: replace it, or keep it.
     525         *
     526         * @param array $pending The waiting connect (code, verifier, base).
     527         */
     528        private function confirm_box( array $pending ) {
     529                $current_name = Mintage_Pay::verified_merchant_name();
     530                $current_mode = Mintage_Pay::is_test_key( (string) $this->get_option( 'api_key' ) ) ? __( 'test mode (sandbox)', 'mintage-pay' ) : __( 'live', 'mintage-pay' );
     531                $new_mode     = Mintage_Pay::SANDBOX_API_BASE === $pending['base'] ? __( 'test mode (sandbox)', 'mintage-pay' ) : __( 'live', 'mintage-pay' );
     532                ?>
     533                <div class="notice notice-warning inline" style="padding:12px 16px;margin:16px 0;">
     534                        <h3 style="margin-top:0;"><?php esc_html_e( 'Replace the Mintage Pay key?', 'mintage-pay' ); ?></h3>
     535                        <p>
     536                                <?php
     537                                if ( '' !== $current_name ) {
     538                                        /* translators: 1: Mintage Pay business name, 2: "live" or "test mode (sandbox)" */
     539                                        echo esc_html( sprintf( __( 'This store takes payments for %1$s (%2$s) now.', 'mintage-pay' ), $current_name, $current_mode ) );
     540                                } else {
     541                                        /* translators: %s: "live" or "test mode (sandbox)" */
     542                                        echo esc_html( sprintf( __( 'This store has a working Mintage Pay key now (%s).', 'mintage-pay' ), $current_mode ) );
     543                                }
     544                                echo ' ';
     545                                /* translators: %s: "live" or "test mode (sandbox)" */
     546                                echo esc_html( sprintf( __( 'You just approved a new connection (%s). Replacing the key sends new payments to the account you approved.', 'mintage-pay' ), $new_mode ) );
     547                                ?>
     548                        </p>
     549                        <?php if ( Mintage_Pay::SANDBOX_API_BASE === $pending['base'] && ! Mintage_Pay::is_test_key( (string) $this->get_option( 'api_key' ) ) ) : ?>
     550                                <p><strong style="color:#b32d2e;"><?php esc_html_e( 'The new key is a test key: customers would pay with test money and no real payment would reach you.', 'mintage-pay' ); ?></strong></p>
     551                        <?php endif; ?>
     552                        <p>
     553                                <a class="button button-primary" href="<?php echo esc_url( wp_nonce_url( admin_url( 'admin-post.php?action=' . Mintage_Pay_Connect::CONFIRM_ACTION ), Mintage_Pay_Connect::CONFIRM_ACTION ) ); ?>"><?php esc_html_e( 'Replace the key', 'mintage-pay' ); ?></a>
     554                                <a class="button" href="<?php echo esc_url( wp_nonce_url( admin_url( 'admin-post.php?action=' . Mintage_Pay_Connect::CANCEL_ACTION ), Mintage_Pay_Connect::CANCEL_ACTION ) ); ?>"><?php esc_html_e( 'Keep the current key', 'mintage-pay' ); ?></a>
     555                        </p>
     556                </div>
     557                <?php
     558        }
     559
     560        /**
     561         * The "Plugin updates" row: WordPress's own auto-update choice for this plugin (the Plugins page's "Enable
     562         * auto-updates" link), shown as a checkbox.
     563         *
     564         * @param string $key  Field key.
     565         * @param array  $data Field data.
     566         * @return string
     567         */
     568        public function generate_mintagepay_auto_update_html( $key, $data ) {
     569                $state     = Mintage_Pay_Connect::auto_update_state();
     570                $field_key = $this->get_field_key( $key );
     571                ob_start();
     572                ?>
     573                <tr valign="top">
     574                        <th scope="row" class="titledesc"><label for="<?php echo esc_attr( $field_key ); ?>"><?php echo esc_html( $data['title'] ); ?></label></th>
     575                        <td class="forminp">
     576                                <fieldset>
     577                                        <label for="<?php echo esc_attr( $field_key ); ?>">
     578                                                <input type="checkbox" name="<?php echo esc_attr( $field_key ); ?>" id="<?php echo esc_attr( $field_key ); ?>" value="1" <?php checked( $state['on'] ); ?> <?php disabled( ! $state['allowed'] ); ?> />
     579                                                <?php esc_html_e( 'Update Mintage Pay automatically when a new version is out', 'mintage-pay' ); ?>
     580                                        </label>
     581                                        <p class="description">
     582                                                <?php
     583                                                if ( $state['allowed'] ) {
     584                                                        esc_html_e( 'The same as "Enable auto-updates" for Mintage Pay on the Plugins page. Recommended: fixes and new payment options reach your checkout without you doing anything. If your host or a tool such as MainWP manages your updates, that may already cover this site.', 'mintage-pay' );
     585                                                } else {
     586                                                        esc_html_e( 'Your host or your site settings manage plugin updates here, or your user cannot update plugins, so this cannot be changed from this page.', 'mintage-pay' );
     587                                                }
     588                                                ?>
     589                                        </p>
     590                                </fieldset>
     591                        </td>
     592                </tr>
     593                <?php
     594                return (string) ob_get_clean();
     595        }
     596
     597        /**
     598         * The auto-update checkbox: 'yes' or 'no' (WordPress's own setting is the one that counts, see process_admin_options()).
     599         *
     600         * @param string $key   Key.
     601         * @param mixed  $value Value.
     602         * @return string
     603         */
     604        public function validate_mintagepay_auto_update_field( $key, $value ) {
     605                return ! empty( $value ) ? 'yes' : 'no';
     606        }
     607
     608        /* ------------------------------------------------------------------ *
     609         * WooCommerce's Payments page (WooCommerce 9.6+ calls these; older versions never do). The card and crypto methods
     610         * share the main method's key, so "set up", "connected" and "test mode" are the same for all three.
     611         * ------------------------------------------------------------------ */
     612
     613        /**
     614         * Needs setup until a key has been checked with Mintage Pay. Not "is usable": that also needs the method switched
     615         * on, and WooCommerce will not switch on a method that says it needs setup.
     616         *
     617         * @return bool
     618         */
     619        public function needs_setup() {
     620                return ! Mintage_Pay::is_key_verified();
     621        }
     622
     623        /**
     624         * Connected: a key is saved and Mintage Pay accepted it.
     625         *
     626         * @return bool
     627         */
     628        public function is_account_connected() {
     629                return Mintage_Pay::is_key_verified();
     630        }
     631
     632        /**
     633         * Test mode: the saved key is a sandbox key.
     634         *
     635         * @return bool
     636         */
     637        public function is_test_mode() {
     638                return Mintage_Pay::is_test_key( (string) Mintage_Pay::setting( 'api_key', '' ) );
     639        }
     640
     641        /**
     642         * This method's own settings page.
     643         *
     644         * @return string
     645         */
     646        public function get_settings_url() {
     647                return admin_url( 'admin.php?page=wc-settings&tab=checkout&section=' . $this->id );
     648        }
     649
     650        /**
     651         * Where "Complete setup" goes: the main method's settings, with the Connect buttons.
     652         *
     653         * @param string $return_url Where WooCommerce would like to come back to (not used: the settings page is the end).
     654         * @return string
     655         */
     656        public function get_connection_url( $return_url = '' ) {
     657                return Mintage_Pay::settings_url();
     658        }
     659
     660        /**
     661         * Links on the Payments page's menu for this method.
     662         *
     663         * @param string $country_code Store country (not used: the same links everywhere).
     664         * @return array[]
     665         */
     666        public function get_provider_links( $country_code = '' ) {
     667                return self::provider_links();
     668        }
     669
     670        /**
     671         * The Payments page links (pure, tested in tests/payments-page-test.php).
     672         *
     673         * @return array[]
     674         */
     675        public static function provider_links() {
     676                return array(
     677                        array(
     678                                '_type' => 'about',
     679                                'url'   => 'https://mintagepay.com/',
     680                        ),
     681                        array(
     682                                '_type' => 'documentation',
     683                                'url'   => 'https://mintagepay.com/docs/integrations/#woocommerce',
     684                        ),
     685                        array(
     686                                '_type' => 'support',
     687                                'url'   => 'https://mintagepay.com/docs/',
     688                        ),
     689                        array(
     690                                '_type' => 'pricing',
     691                                'url'   => 'https://mintagepay.com/#pricing',
     692                        ),
     693                        array(
     694                                '_type' => 'terms',
     695                                'url'   => 'https://mintagepay.com/terms.html',
     696                        ),
     697                );
     698        }
     699
     700        /**
    448701         * The webhook row stores nothing.
    449702         *
    … …  
    521774
    522775        /**
    523          * Save settings, then validate a new key with GET /v1/me and register the webhook.
     776         * Save settings, then validate a new key with GET /v1/me and register the webhook (Mintage_Pay_Admin::activate_key(),
     777         * the same steps "Connect to Mintage Pay" runs), and apply the auto-update choice.
    524778         *
    525779         * @return bool
    … …  
    536790                $this->enabled     = $this->get_option( 'enabled' );
    537791
    538                 $new_key  = (string) $this->get_option( 'api_key' );
    539                 $new_base = Mintage_Pay::api_base_for( $new_key, (string) $this->get_option( 'api_base_url' ) );
    540                 delete_transient( Mintage_Pay::RAILS_TRANSIENT );
    541                 $old_api  = '' !== $old_key ? Mintage_Pay::api( $old_key, $old_base ) : null;
    542 
    543                 if ( '' === $new_key ) {
    544                         // Key removed: disable the old endpoint and forget the secret.
    545                         $hook = Mintage_Pay::get_webhook();
    546                         if ( $hook && $old_api && isset( $hook['fingerprint'] ) && Mintage_Pay::fingerprint( $old_base, $old_key ) === $hook['fingerprint'] ) {
    547                                 $old_api->delete_webhook( $hook['id'] );
    548                         }
    549                         Mintage_Pay::clear_webhook();
    550                         Mintage_Pay::clear_key_verified();
    551                         return $saved;
    552                 }
    553 
    554                 $credentials_changed = ( $new_key !== $old_key || $new_base !== $old_base );
    555                 $hook                = Mintage_Pay::get_webhook();
    556                 $fingerprint         = Mintage_Pay::fingerprint( $new_base, $new_key );
    557                 $hook_ok             = $hook && ! empty( $hook['secret'] ) && isset( $hook['fingerprint'] ) && $hook['fingerprint'] === $fingerprint;
    558 
    559                 if ( ! $credentials_changed && $hook_ok && Mintage_Pay::is_key_verified() ) {
    560                         return $saved;
    561                 }
    562 
    563                 $api = Mintage_Pay::api( $new_key, $new_base );
    564                 $me  = $api->get_me();
    565                 if ( is_wp_error( $me ) ) {
    566                         // A new key is unverified by definition (the stored flag holds another key's fingerprint).
    567                         Mintage_Pay_Admin::handle_me_error( $me );
    568                         Mintage_Pay::add_notice(
    569                                 'error',
    570                                 sprintf(
    571                                         /* translators: %s: error message */
    572                                         __( 'Mintage Pay: the API key could not be verified (%s). The payment method stays hidden at checkout until the key is verified. Check the key and save again.', 'mintage-pay' ),
    573                                         Mintage_Pay_Admin::me_error_text( $me, $new_key, (string) $this->get_option( 'api_base_url' ) )
    574                                 )
    575                         );
    576                         return $saved;
    577                 }
    578 
    579                 $merchant_id = Mintage_Pay_Admin::merchant_id( $me );
    580                 $name        = isset( $me['merchant']['name'] ) && is_string( $me['merchant']['name'] ) ? $me['merchant']['name'] : '';
    581                 Mintage_Pay::mark_key_verified( $api->fingerprint(), $merchant_id, $name );
    582                 Mintage_Pay::cache_rails( $me );
    583 
    584                 if ( $hook_ok ) {
    585                         Mintage_Pay::add_notice( 'success', $this->connected_message( $name ) );
    586                         return $saved;
    587                 }
    588 
    589                 $previous_api = null;
    590                 if ( $hook && isset( $hook['fingerprint'] ) ) {
    591                         if ( $old_api && Mintage_Pay::fingerprint( $old_base, $old_key ) === $hook['fingerprint'] ) {
    592                                 $previous_api = $old_api;
    593                         } elseif ( $hook['fingerprint'] === $fingerprint ) {
    594                                 $previous_api = $api;
    595                         }
    596                 }
    597 
    598                 $result = Mintage_Pay_Admin::connect_webhook( $api, $previous_api, $merchant_id );
    599                 if ( true === $result ) {
    600                         Mintage_Pay::add_notice( 'success', $this->connected_message( $name ) . ' ' . __( 'Webhook connected.', 'mintage-pay' ) );
    601                 }
     792                Mintage_Pay_Admin::activate_key( $old_key, $old_base, (string) $this->get_option( 'api_key' ), (string) $this->get_option( 'api_base_url' ) );
     793                Mintage_Pay_Connect::save_auto_update( 'yes' === $this->get_option( 'auto_update' ) );
    602794                return $saved;
    603         }
    604 
    605         /**
    606          * "Connected to Mintage Pay as X."
    607          *
    608          * @param string $name Merchant name.
    609          * @return string
    610          */
    611         private function connected_message( $name ) {
    612                 $mode = Mintage_Pay::is_test_key( (string) $this->get_option( 'api_key' ) )
    613                         ? __( 'TEST MODE: sandbox, test money only', 'mintage-pay' )
    614                         : __( 'live', 'mintage-pay' );
    615                 return '' !== $name
    616                         /* translators: 1: merchant business name, 2: "live" or "TEST MODE: ..." */
    617                         ? sprintf( __( 'Mintage Pay: API key verified for %1$s (%2$s).', 'mintage-pay' ), $name, $mode )
    618                         /* translators: %s: "live" or "TEST MODE: ..." */
    619                         : sprintf( __( 'Mintage Pay: API key verified (%s).', 'mintage-pay' ), $mode );
    620795        }
    621796
  • mintage-pay/tags/1.18.0/mintage-pay.php

    r3768248 r3768791  
    44 * Plugin URI:           https://mintagepay.com/docs/integrations/#woocommerce
    55 * Description:          Accept USDC and USDT stablecoin payments through the Mintage Pay hosted checkout, with prices in USD, EUR, GBP and more.
    6  * Version:              1.17.0
     6 * Version:              1.18.0
    77 * Requires at least:    6.4
    88 * Requires PHP:         7.4
    … …  
    1414 * Text Domain:          mintage-pay
    1515 * WC requires at least: 8.0
    16  * WC tested up to:      11.1
     16 * WC tested up to:      11.2
    1717 *
    1818 * @package MintagePay
    … …  
    2121defined( 'ABSPATH' ) || exit;
    2222
    23 define( 'MINTAGE_PAY_VERSION', '1.17.0' );
     23define( 'MINTAGE_PAY_VERSION', '1.18.0' );
    2424define( 'MINTAGE_PAY_FILE', __FILE__ );
    2525define( 'MINTAGE_PAY_PATH', plugin_dir_path( __FILE__ ) );
    … …  
    153153                require_once MINTAGE_PAY_PATH . 'includes/class-mintagepay-reminder.php';
    154154                require_once MINTAGE_PAY_PATH . 'includes/class-mintagepay-two-rail.php';
     155                require_once MINTAGE_PAY_PATH . 'includes/class-mintagepay-connect.php';
    155156
    156157                add_filter( 'woocommerce_payment_gateways', array( __CLASS__, 'add_gateway' ) );
    … …  
    176177                Mintage_Pay_Discount::init();
    177178                Mintage_Pay_Reminder::init();
     179                Mintage_Pay_Connect::init();
    178180        }
    179181
  • mintage-pay/tags/1.18.0/readme.txt

    r3768368 r3768791  
    77WC requires at least: 8.0
    88WC tested up to: 11.2
    9 Stable tag: 1.17.0
     9Stable tag: 1.18.0
    1010License: GPLv2 or later
    1111License URI: https://www.gnu.org/licenses/gpl-2.0.html
    … …  
    2121* **Pay by app**: Cash App in one tap, plus Apple Pay and Google Pay where Mintage Pay offers them for the total.
    2222
    23 **Get started in minutes:** install, try it with test money in the sandbox, then apply for a live account. See Installation below, or the full guide at https://mintagepay.com/docs/integrations/#woocommerce
     23**Get started in minutes:** install, click "Connect to Mintage Pay" and approve your store. No key to copy. Try it with test money in the sandbox first if you like, then apply for a live account. See Installation below, or the full guide at https://mintagepay.com/docs/integrations/#woocommerce
    2424
    2525Mintage Pay sends your customer to a secure hosted checkout where they pay in stablecoins (USDC or USDT) from a wallet or an exchange, on the networks you turn on in your Mintage Pay dashboard (for example Base, Polygon, Solana or TRON). A network appears at checkout only when you have set a payout address for it.
    … …  
    5353What is sent, and when:
    5454
     55* When you click "Connect to Mintage Pay" (or "Connect a test (sandbox) account"): your browser opens app.mintagepay.com (or sandbox.mintagepay.com) with this site's address, its name, the plugin version, a random value and a code challenge (PKCE). You sign in there and approve this store. Your browser then comes back to this site with a one-time code, and the plugin sends that code and its secret to Mintage Pay to receive this store's API key. Nothing is sent before you click.
    5556* When you save the settings, reconnect the webhook, or activate, deactivate or delete the plugin: your API key (in the Authorization header), this store's webhook URL and the store name, to check the key and to add or remove the webhook endpoint.
    5657* When a customer chooses Mintage Pay at checkout: the order total and currency, the order number, a description ("Order #1042 from Your Store"), the thank-you and cancel page addresses, the WooCommerce order id and order key, the invoice expiry, and the customer's billing email address. Mintage Pay emails the customer a receipt only if you turn off "Let WooCommerce send the order emails"; otherwise it writes to them only when they ask for a refund on Mintage Pay's refund page. For an order paid by card, the email is also kept as part of the record of that card payment, in case the customer disputes the charge.
    … …  
    7475**3. Go live.** Apply at https://mintagepay.com/#apply. Once your account is approved, sign in at https://app.mintagepay.com, set a payout address for each network you want to be paid on, and create a live key (mp_live_...).
    7576
    76 **4. Connect your store.** Go to WooCommerce > Settings > Payments > Mintage Pay, paste the key, tick Enable and save. Saving checks the key and registers this store's webhook automatically. The settings page shows "Connected to (your business name) (live)" when it is done, or "(TEST MODE: sandbox, test money only)" for a sandbox key.
    77 
    78 To try it first, use a sandbox key (mp_test_...) from https://sandbox.mintagepay.com. Leave "API base URL" empty: the key decides whether the plugin talks to the sandbox or the live service. Replace it with a live key (mp_live_...) before you take real orders.
     77**4. Connect your store.** Go to WooCommerce > Settings > Payments > Mintage Pay and click "Connect to Mintage Pay" (or "Connect a test (sandbox) account"). Sign in to Mintage Pay, check that the store address shown is yours, and click Approve. You come back to WordPress with the key saved, the webhook registered and the payment method switched on. You can also paste a key from Developers > API keys instead: tick Enable and save. Saving checks the key and registers this store's webhook automatically. The settings page shows "Connected to (your business name) (live)" when it is done, or "(TEST MODE: sandbox, test money only)" for a sandbox key.
     78
     79To try it first, connect a sandbox account, or paste a sandbox key (mp_test_...) from https://sandbox.mintagepay.com. Click "Connect to Mintage Pay" again later to switch to your live account: the plugin asks before it replaces a working key. Leave "API base URL" empty: the key decides whether the plugin talks to the sandbox or the live service. Replace it with a live key (mp_live_...) before you take real orders.
    7980
    8081If you move the store to a new address or the webhook shows "Not connected", click "Reconnect webhook" on the settings page.
    … …  
    8283**5. Choose your methods.** Under WooCommerce > Settings > Payments, switch on "Mintage Pay: Pay by card" and "Mintage Pay: Pay with crypto" if you want them as their own methods, and drag the methods into the order you want.
    8384
    84 **6. Stay up to date automatically.** On the Plugins screen, click "Enable auto-updates" next to Mintage Pay. Fixes and new payment options then install on their own.
     85**6. Stay up to date automatically.** Tick "Update Mintage Pay automatically" on the settings page, or click "Enable auto-updates" next to Mintage Pay on the Plugins screen (it is the same setting). Fixes and new payment options then install on their own.
    8586
    8687= Documentation =
    … …  
    100101Yes. A sandbox account for testing is free and instant at https://sandbox.mintagepay.com. A live account is by application at https://mintagepay.com/#apply.
    101102
     103= What does "Connect to Mintage Pay" do? =
     104
     105It makes an API key for this store in your Mintage Pay account and saves it here, so you never copy a key by hand. You approve it on Mintage Pay's own site, signed in to your account, and the page shows the store address that asked. Only approve if you just clicked Connect in your own WordPress. The key appears under Developers > API keys as "WooCommerce (your store address)" and the account owner gets an email. If you connect the same store again, the key from the earlier Connect is revoked as soon as the new one is used. Keys you made by hand are never touched.
     106
    102107= Why don't I see Pay by card or Pay by app? =
    103108
    … …  
    151156
    152157== Changelog ==
     158
     159= 1.18.0 =
     160* New: "Connect to Mintage Pay". Click it on the settings page, sign in to Mintage Pay and approve your store: the API key is saved, the webhook is registered and the payment method is switched on. No key to copy. "Connect a test (sandbox) account" does the same with test money. If a working key is already saved, the plugin asks before replacing it.
     161* New: a "Get started" box on the settings page while no key is saved, with Connect, the sandbox, the application form and the setup guide.
     162* New: "Update Mintage Pay automatically" on the settings page (WordPress's own auto-update setting for this plugin).
     163* WooCommerce's Payments page now shows the Mintage Pay icon, says the method needs setup until a key is verified and shows test mode for a sandbox key, and links to the guide, pricing and terms. Each Mintage Pay method opens its own settings.
     164* Fix: with only the main method on, its row showed the Cash App, Apple Pay and Google Pay icons even where Mintage Pay does not offer those apps for the store. The icons now follow what Mintage Pay offers, on the classic and the block checkout.
     165* Tested with WooCommerce 11.2.
    153166
    154167= 1.17.0 =
    … …  
    269282= 1.0.0 =
    270283* First release.
     284
     285== Upgrade Notice ==
     286
     287= 1.18.0 =
     288One-click "Connect to Mintage Pay" (no key to copy), a Get started box, an "Update Mintage Pay automatically" setting, and a fix for app icons shown where the apps are not offered.
  • mintage-pay/trunk/includes/class-mintagepay-admin.php

    r3768248 r3768791  
    166166         * Webhook connection
    167167         * ------------------------------------------------------------------ */
     168
     169        /**
     170         * After the API key was saved (the settings form, or "Connect to Mintage Pay", 1.18.0): check the new key with
     171         * GET /v1/me, remember it as verified, and connect the webhook (deleting the old key's endpoint). The same steps for
     172         * both, so a connected store is set up exactly like one where the key was pasted.
     173         *
     174         * @param string $old_key      The key saved before.
     175         * @param string $old_base     The API base the old key used (Mintage_Pay::api_base_for()).
     176         * @param string $new_key      The key saved now ('' = removed).
     177         * @param string $base_setting The saved "API base URL" setting ('' = automatic).
     178         * @return string 'removed', 'unchanged', 'verified' (the webhook was already connected), 'connected', or 'failed'.
     179         */
     180        public static function activate_key( $old_key, $old_base, $new_key, $base_setting ) {
     181                $old_key  = (string) $old_key;
     182                $new_key  = (string) $new_key;
     183                $new_base = Mintage_Pay::api_base_for( $new_key, (string) $base_setting );
     184                delete_transient( Mintage_Pay::RAILS_TRANSIENT );
     185                $old_api = '' !== $old_key ? Mintage_Pay::api( $old_key, $old_base ) : null;
     186
     187                if ( '' === $new_key ) {
     188                        // Key removed: disable the old endpoint and forget the secret.
     189                        $hook = Mintage_Pay::get_webhook();
     190                        if ( $hook && $old_api && isset( $hook['fingerprint'] ) && Mintage_Pay::fingerprint( $old_base, $old_key ) === $hook['fingerprint'] ) {
     191                                $old_api->delete_webhook( $hook['id'] );
     192                        }
     193                        Mintage_Pay::clear_webhook();
     194                        Mintage_Pay::clear_key_verified();
     195                        return 'removed';
     196                }
     197
     198                $credentials_changed = ( $new_key !== $old_key || $new_base !== $old_base );
     199                $hook                = Mintage_Pay::get_webhook();
     200                $fingerprint         = Mintage_Pay::fingerprint( $new_base, $new_key );
     201                $hook_ok             = $hook && ! empty( $hook['secret'] ) && isset( $hook['fingerprint'] ) && $hook['fingerprint'] === $fingerprint;
     202
     203                if ( ! $credentials_changed && $hook_ok && Mintage_Pay::is_key_verified() ) {
     204                        return 'unchanged';
     205                }
     206
     207                $api = Mintage_Pay::api( $new_key, $new_base );
     208                $me  = $api->get_me();
     209                if ( is_wp_error( $me ) ) {
     210                        // A new key is unverified by definition (the stored flag holds another key's fingerprint).
     211                        self::handle_me_error( $me );
     212                        Mintage_Pay::add_notice(
     213                                'error',
     214                                sprintf(
     215                                        /* translators: %s: error message */
     216                                        __( 'Mintage Pay: the API key could not be verified (%s). The payment method stays hidden at checkout until the key is verified. Check the key and save again.', 'mintage-pay' ),
     217                                        self::me_error_text( $me, $new_key, (string) $base_setting )
     218                                )
     219                        );
     220                        return 'failed';
     221                }
     222
     223                $merchant_id = self::merchant_id( $me );
     224                $name        = self::merchant_name( $me );
     225                Mintage_Pay::mark_key_verified( $api->fingerprint(), $merchant_id, $name );
     226                Mintage_Pay::cache_rails( $me );
     227
     228                if ( $hook_ok ) {
     229                        Mintage_Pay::add_notice( 'success', self::connected_message( $name, $new_key ) );
     230                        return 'verified';
     231                }
     232
     233                $previous_api = null;
     234                if ( $hook && isset( $hook['fingerprint'] ) ) {
     235                        if ( $old_api && Mintage_Pay::fingerprint( $old_base, $old_key ) === $hook['fingerprint'] ) {
     236                                $previous_api = $old_api;
     237                        } elseif ( $hook['fingerprint'] === $fingerprint ) {
     238                                $previous_api = $api;
     239                        }
     240                }
     241
     242                $result = self::connect_webhook( $api, $previous_api, $merchant_id );
     243                if ( true === $result ) {
     244                        Mintage_Pay::add_notice( 'success', self::connected_message( $name, $new_key ) . ' ' . __( 'Webhook connected.', 'mintage-pay' ) );
     245                        return 'connected';
     246                }
     247                return 'failed';
     248        }
     249
     250        /**
     251         * "Mintage Pay: API key verified for X (live)."
     252         *
     253         * @param string $name    Merchant name.
     254         * @param string $api_key The key (test or live).
     255         * @return string
     256         */
     257        public static function connected_message( $name, $api_key ) {
     258                $mode = Mintage_Pay::is_test_key( (string) $api_key )
     259                        ? __( 'TEST MODE: sandbox, test money only', 'mintage-pay' )
     260                        : __( 'live', 'mintage-pay' );
     261                return '' !== $name
     262                        /* translators: 1: merchant business name, 2: "live" or "TEST MODE: ..." */
     263                        ? sprintf( __( 'Mintage Pay: API key verified for %1$s (%2$s).', 'mintage-pay' ), $name, $mode )
     264                        /* translators: %s: "live" or "TEST MODE: ..." */
     265                        : sprintf( __( 'Mintage Pay: API key verified (%s).', 'mintage-pay' ), $mode );
     266        }
    168267
    169268        /**
  • mintage-pay/trunk/includes/class-mintagepay-blocks.php

    r3768248 r3768791  
    105105
    106106        /**
    107          * The apps' marks for this method's own row (WC_Gateway_MintagePay::get_icon() for the classic checkout): Cash App,
    108          * plus Apple Pay / Google Pay where Mintage Pay offers the card route for the cart's total. None while the two
    109          * choices are inside the method (the marks are on the choices then) or when it is the only rail.
     107         * The apps' marks for this method's own row (WC_Gateway_MintagePay::get_icon() for the classic checkout), by the one
     108         * rule in Mintage_Pay_Two_Rail::row_marks(): only what Pay by app offers this store for the cart's total, none while
     109         * the two choices are inside the method (the marks are on the choices then).
    110110         *
    111111         * @return array[]
    … …  
    117117                $currency = function_exists( 'get_woocommerce_currency' ) ? (string) get_woocommerce_currency() : 'USD';
    118118                $total    = $this->cart_total();
    119                 $apps     = Mintage_Pay::split_on()
    120                         || ( Mintage_Pay::crypto_on() && ! ( $total > 0 && Mintage_Pay_Two_Rail::offered( $total, $currency ) ) );
    121                 if ( ! $apps ) {
    122                         return array();
    123                 }
    124                 $wallets = $total > 0 ? Mintage_Pay_Two_Rail::card_status( $total, $currency )['available'] : true;
    125                 return Mintage_Pay_Two_Rail::mark_images( 'app', $wallets );
     119                // The same rule as the classic checkout (Mintage_Pay_Two_Rail::row_marks()); no cart (the editor): $total is 0.
     120                return Mintage_Pay_Two_Rail::row_mark_images( $total, $currency, $total <= 0 );
    126121        }
    127122
  • mintage-pay/trunk/includes/class-mintagepay-two-rail.php

    r3768248 r3768791  
    223223         */
    224224        public static function card_status( $total, $currency ) {
    225                 $none     = self::card_from_options( null );
     225                return self::status( $total, $currency )['card'];
     226        }
     227
     228        /**
     229         * Read `options.cash_app` and `options.wallet` of a GET /v1/payment-options response (1.18.0): whether Pay by app
     230         * offers Cash App, and crypto, for this store and total. Pure. A server that does not send them offers neither.
     231         *
     232         * @param mixed $options Response.
     233         * @return array { cash_app: bool, wallet: bool }
     234         */
     235        public static function apps_from_options( $options ) {
     236                $o = is_array( $options ) && isset( $options['options'] ) && is_array( $options['options'] ) ? $options['options'] : array();
     237                return array(
     238                        'cash_app' => isset( $o['cash_app'] ) && true === $o['cash_app'],
     239                        'wallet'   => isset( $o['wallet'] ) && true === $o['wallet'],
     240                );
     241        }
     242
     243        /**
     244         * The card route and the apps for an amount, from one GET /v1/payment-options (card_status() caching: 10 minutes per
     245         * amount, 2 after a failure). Unknown (no verified key, the API failed): no card, no apps.
     246         *
     247         * @param float|string $total    Order total in the store currency.
     248         * @param string       $currency ISO 4217.
     249         * @return array { card: card_from_options(), apps: apps_from_options() }
     250         */
     251        public static function status( $total, $currency ) {
     252                $none     = array(
     253                        'card' => self::card_from_options( null ),
     254                        'apps' => self::apps_from_options( null ),
     255                );
    226256                $currency = strtoupper( (string) $currency );
    227257                if ( ! class_exists( 'Mintage_Pay_Webhook' ) || ! Mintage_Pay_Webhook::currency_supported( $currency ) || ! Mintage_Pay::is_key_verified() ) {
    … …  
    238268                $key    = self::TRANSIENT . md5( $api->fingerprint() . '|' . $currency . '|' . $minor );
    239269                $cached = get_transient( $key );
    240                 if ( is_array( $cached ) && isset( $cached['available'] ) ) {
    241                         return self::card_from_options( array( 'card' => $cached ) );
     270                // A value cached by 1.17.0 has no 'apps': asked again.
     271                if ( is_array( $cached ) && isset( $cached['available'] ) && isset( $cached['apps'] ) && is_array( $cached['apps'] ) ) {
     272                        return array(
     273                                'card' => self::card_from_options( array( 'card' => $cached ) ),
     274                                'apps' => self::apps_from_options( array( 'options' => $cached['apps'] ) ),
     275                        );
    242276                }
    243277                $options = $api->payment_options( Mintage_Pay_Webhook::format_minor( $minor, $currency ), $currency, 5 );
    244278                $card    = self::card_from_options( is_wp_error( $options ) ? null : $options );
    245                 set_transient( $key, $card, is_wp_error( $options ) ? 2 * MINUTE_IN_SECONDS : 10 * MINUTE_IN_SECONDS );
     279                $apps    = self::apps_from_options( is_wp_error( $options ) ? null : $options );
     280                set_transient( $key, $card + array( 'apps' => $apps ), is_wp_error( $options ) ? 2 * MINUTE_IN_SECONDS : 10 * MINUTE_IN_SECONDS );
    246281                if ( is_wp_error( $options ) ) {
    247282                        Mintage_Pay::log( 'payment options could not be read (the card choice stays hidden for now): ' . $options->get_error_message(), 'warning' );
    248283                }
    249                 return $card;
     284                return array(
     285                        'card' => $card,
     286                        'apps' => $apps,
     287                );
     288        }
     289
     290        /**
     291         * The marks on the main method's own row at checkout (classic get_icon() and the block checkout's data use this
     292         * one rule, 1.18.0). Pure.
     293         *  - The two choices are inside the method: none (the marks are on the choices).
     294         *  - No cart (admin, the block editor): the apps' marks where the method is the apps (split, or crypto its own
     295         *    method), none otherwise, as before 1.18.0.
     296         *  - Otherwise only where Pay by app offers Cash App for this store (options.cash_app): Cash App, then Apple Pay and
     297         *    Google Pay where the card route is offered, then USDC and USDT where crypto is inside Pay by app (crypto not its
     298         *    own method) and offered (options.wallet). A store without Cash App, or a failed read, shows none.
     299         *
     300         * @param bool       $split   "Pay by card" is its own method (Mintage_Pay::split_on()).
     301         * @param bool       $crypto  "Pay with crypto" is its own method (Mintage_Pay::crypto_on()).
     302         * @param bool       $offered The two choices are offered inside the method for this total.
     303         * @param array|null $status  status() for the cart's total, or null when there is no cart.
     304         * @return array[] marks() entries.
     305         */
     306        public static function row_marks( $split, $crypto, $offered, $status ) {
     307                if ( ! $split && $offered ) {
     308                        return array();
     309                }
     310                if ( null === $status ) {
     311                        return ( $split || $crypto ) ? self::marks( 'app', true ) : array();
     312                }
     313                if ( empty( $status['apps']['cash_app'] ) ) {
     314                        return array();
     315                }
     316                return self::marks( 'app', ! empty( $status['card']['available'] ), ! $crypto && ! empty( $status['apps']['wallet'] ) );
     317        }
     318
     319        /**
     320         * row_marks() for this store and cart, as image addresses.
     321         *
     322         * @param float|string $total    Cart or order total.
     323         * @param string       $currency ISO 4217.
     324         * @param bool         $no_cart  There is no cart (admin, the block editor).
     325         * @return array[]
     326         */
     327        public static function row_mark_images( $total, $currency, $no_cart ) {
     328                $status  = $no_cart ? null : self::status( $total, $currency );
     329                $offered = ! $no_cart && self::offered( $total, $currency );
     330                return self::images( self::row_marks( Mintage_Pay::split_on(), Mintage_Pay::crypto_on(), $offered, $status ) );
    250331        }
    251332
    … …  
    329410         * @param string $choice  'card', 'app' or 'crypto'.
    330411         * @param bool   $wallets Apple Pay and Google Pay are offered on Pay by app.
     412         * @param bool   $coins   USDC and USDT after the apps (Pay by app with crypto inside it, row_marks()).
    331413         * @return array[]
    332414         */
    333         public static function marks( $choice, $wallets = true ) {
     415        public static function marks( $choice, $wallets = true, $coins = false ) {
    334416                if ( 'card' === $choice ) {
    335417                        return array(
    … …  
    350432                        $out[] = array( 'google-pay', 'Google Pay', 45 );
    351433                }
     434                if ( $coins ) {
     435                        $out[] = array( 'usdc', 'USDC', 38 );
     436                        $out[] = array( 'usdt', 'USDT', 38 );
     437                }
    352438                return $out;
    353439        }
    … …  
    361447         */
    362448        public static function mark_images( $choice, $wallets = true ) {
     449                return self::images( self::marks( $choice, $wallets ) );
     450        }
     451
     452        /**
     453         * marks() entries as addresses: file, label, width.
     454         *
     455         * @param array[] $marks marks() entries.
     456         * @return array[]
     457         */
     458        public static function images( array $marks ) {
    363459                $base = plugins_url( 'assets/img/marks/', MINTAGE_PAY_FILE );
    364460                $out  = array();
    365                 foreach ( self::marks( $choice, $wallets ) as $m ) {
     461                foreach ( $marks as $m ) {
    366462                        $out[] = array(
    367463                                'src'   => $base . $m[0] . '.svg?ver=' . MINTAGE_PAY_VERSION,
    … …  
    382478         */
    383479        public static function marks_html( $choice, $wallets = true ) {
     480                return self::images_html( self::mark_images( $choice, $wallets ) );
     481        }
     482
     483        /**
     484         * Image addresses (images()) as the payment method row's HTML.
     485         *
     486         * @param array[] $images images() entries.
     487         * @return string
     488         */
     489        public static function images_html( array $images ) {
    384490                $html = '';
    385                 foreach ( self::mark_images( $choice, $wallets ) as $m ) {
     491                foreach ( $images as $m ) {
    386492                        $html .= '<img class="mintagepay-method-mark" src="' . esc_url( $m['src'] ) . '" alt="' . esc_attr( $m['alt'] ) . '" title="' . esc_attr( $m['alt'] ) . '" width="' . (int) $m['width'] . '" height="24" decoding="async" />';
    387493                }
  • mintage-pay/trunk/includes/class-wc-gateway-mintagepay.php

    r3768248 r3768791  
    2727                // claiming a refund was sent (WooCommerce would otherwise only offer "Refund manually").
    2828                $this->supports           = array_merge( Mintage_Pay_Subscriptions::supports(), array( 'refunds' ) );
     29                // The square Mintage Pay mark for WooCommerce's Payments page (it reads this property, over REST too). The
     30                // checkout row never shows it: get_icon() below does not call parent::get_icon().
     31                $this->icon               = plugins_url( 'assets/img/mintage-pay.svg', MINTAGE_PAY_FILE );
    2932
    3033                $this->init_form_fields();
    … …  
    8083        public function get_icon() {
    8184                $rail = $this->method_rail();
    82                 // With "Pay with crypto" as its own method (1.17.0) this method is the apps, unless it shows the two choices
    83                 // inside itself (their marks are on the choices then).
    84                 if ( '' === $rail && Mintage_Pay::crypto_on() && ! $this->two_rail_offered() ) {
    85                         $rail = 'app';
    86                 }
    87                 if ( '' === $rail ) {
    88                         return parent::get_icon();
    89                 }
    90                 return apply_filters( 'woocommerce_gateway_icon', Mintage_Pay_Two_Rail::marks_html( $rail, $this->wallets_offered() ), $this->id );
     85                if ( 'card' === $rail || 'crypto' === $rail ) {
     86                        return apply_filters( 'woocommerce_gateway_icon', Mintage_Pay_Two_Rail::marks_html( $rail, $this->wallets_offered() ), $this->id );
     87                }
     88                // This method (Pay by app): one rule with the block checkout (Mintage_Pay_Two_Rail::row_marks(), 1.18.0). Never
     89                // parent::get_icon(): $this->icon is set for the WooCommerce Payments page only, not for the checkout row.
     90                $no_cart  = is_admin() && ! wp_doing_ajax();
     91                $currency = function_exists( 'get_woocommerce_currency' ) ? (string) get_woocommerce_currency() : 'USD';
     92                $images   = Mintage_Pay_Two_Rail::row_mark_images( $no_cart ? 0 : $this->get_order_total(), $currency, $no_cart );
     93                return apply_filters( 'woocommerce_gateway_icon', Mintage_Pay_Two_Rail::images_html( $images ), $this->id );
    9194        }
    9295
    … …  
    306309                                'title'       => __( 'API key', 'mintage-pay' ),
    307310                                'type'        => 'password',
    308                                 'description' => __( 'Create one in the Mintage Pay dashboard under Developers > API keys. Saving a new key checks it and connects the webhook automatically.', 'mintage-pay' ),
     311                                'description' => __( 'Easiest: click "Connect to Mintage Pay" at the top of this page and the key is filled in for you. Or create one in the Mintage Pay dashboard under Developers > API keys and paste it here. Saving a new key checks it and connects the webhook automatically.', 'mintage-pay' ),
    309312                                'default'     => '',
    310313                        ),
    … …  
    366369                                'type'  => 'mintagepay_webhook',
    367370                        ),
     371                        'auto_update'        => array(
     372                                'title' => __( 'Plugin updates', 'mintage-pay' ),
     373                                'type'  => 'mintagepay_auto_update',
     374                        ),
    368375                        'debug'              => array(
    369376                                'title'       => __( 'Debug log', 'mintage-pay' ),
    … …  
    399406                        <td class="forminp">
    400407                                <?php if ( '' === (string) $this->get_option( 'api_key' ) ) : ?>
    401                                         <p><?php esc_html_e( 'Not connected. Save an API key first.', 'mintage-pay' ); ?></p>
     408                                        <p><?php esc_html_e( 'Not connected yet.', 'mintage-pay' ); ?></p>
     409                                        <p><?php $this->connect_buttons(); ?></p>
    402410                                <?php else : ?>
    403411                                        <?php if ( $hook && ! empty( $hook['secret'] ) && isset( $hook['fingerprint'] ) && $hook['fingerprint'] === $fingerprint ) : ?>
    … …  
    446454
    447455        /**
     456         * The two Connect buttons (live account, sandbox test account).
     457         */
     458        protected function connect_buttons() {
     459                ?>
     460                <a class="button button-primary" href="<?php echo esc_url( Mintage_Pay_Connect::start_url( 'live' ) ); ?>"><?php esc_html_e( 'Connect to Mintage Pay', 'mintage-pay' ); ?></a>
     461                <a class="button" href="<?php echo esc_url( Mintage_Pay_Connect::start_url( 'test' ) ); ?>"><?php esc_html_e( 'Connect a test (sandbox) account', 'mintage-pay' ); ?></a>
     462                <?php
     463        }
     464
     465        /**
     466         * Settings page: the "Get started" panel (no key yet) or the "Replace the key?" box (a Connect came back while a
     467         * working key is saved), then the settings. Only on this method's own page, not the card or crypto method's.
     468         */
     469        public function admin_options() {
     470                if ( Mintage_Pay::GATEWAY_ID === $this->id ) {
     471                        $pending = Mintage_Pay_Connect::pending();
     472                        if ( $pending ) {
     473                                $this->confirm_box( $pending );
     474                        } elseif ( '' === (string) $this->get_option( 'api_key' ) ) {
     475                                $this->get_started_panel();
     476                        }
     477                }
     478                parent::admin_options();
     479        }
     480
     481        /**
     482         * "Get started": connect, try the sandbox, apply, read the guide.
     483         */
     484        private function get_started_panel() {
     485                ?>
     486                <div class="notice notice-info inline" style="padding:12px 16px;margin:16px 0;">
     487                        <h3 style="margin-top:0;"><?php esc_html_e( 'Get started with Mintage Pay', 'mintage-pay' ); ?></h3>
     488                        <ol style="margin-left:1.5em;">
     489                                <li><?php esc_html_e( 'Have a Mintage Pay account? Click "Connect to Mintage Pay", sign in and approve this store. The API key and the webhook are set up for you and the payment method is switched on.', 'mintage-pay' ); ?></li>
     490                                <li>
     491                                        <?php
     492                                        printf(
     493                                                /* translators: %s: link to the Mintage Pay application form */
     494                                                esc_html__( 'No account yet? %s (a few minutes).', 'mintage-pay' ),
     495                                                '<a href="https://mintagepay.com/#apply" target="_blank" rel="noopener noreferrer">' . esc_html__( 'Apply for a Mintage Pay account', 'mintage-pay' ) . '</a>'
     496                                        );
     497                                        ?>
     498                                </li>
     499                                <li>
     500                                        <?php
     501                                        printf(
     502                                                /* translators: %s: link to the sandbox */
     503                                                esc_html__( 'Want to try it first? Make a free test account on the %s, then click "Connect a test (sandbox) account". Test orders use test money only.', 'mintage-pay' ),
     504                                                '<a href="https://sandbox.mintagepay.com/" target="_blank" rel="noopener noreferrer">' . esc_html__( 'Mintage Pay sandbox', 'mintage-pay' ) . '</a>'
     505                                        );
     506                                        ?>
     507                                </li>
     508                        </ol>
     509                        <p><?php $this->connect_buttons(); ?></p>
     510                        <p class="description">
     511                                <?php
     512                                printf(
     513                                        /* translators: %s: link to the setup guide */
     514                                        esc_html__( 'Step by step: %s.', 'mintage-pay' ),
     515                                        '<a href="https://mintagepay.com/docs/integrations/#woocommerce" target="_blank" rel="noopener noreferrer">' . esc_html__( 'the WooCommerce setup guide', 'mintage-pay' ) . '</a>'
     516                                );
     517                                ?>
     518                        </p>
     519                </div>
     520                <?php
     521        }
     522
     523        /**
     524         * A Connect came back while a working key is saved: replace it, or keep it.
     525         *
     526         * @param array $pending The waiting connect (code, verifier, base).
     527         */
     528        private function confirm_box( array $pending ) {
     529                $current_name = Mintage_Pay::verified_merchant_name();
     530                $current_mode = Mintage_Pay::is_test_key( (string) $this->get_option( 'api_key' ) ) ? __( 'test mode (sandbox)', 'mintage-pay' ) : __( 'live', 'mintage-pay' );
     531                $new_mode     = Mintage_Pay::SANDBOX_API_BASE === $pending['base'] ? __( 'test mode (sandbox)', 'mintage-pay' ) : __( 'live', 'mintage-pay' );
     532                ?>
     533                <div class="notice notice-warning inline" style="padding:12px 16px;margin:16px 0;">
     534                        <h3 style="margin-top:0;"><?php esc_html_e( 'Replace the Mintage Pay key?', 'mintage-pay' ); ?></h3>
     535                        <p>
     536                                <?php
     537                                if ( '' !== $current_name ) {
     538                                        /* translators: 1: Mintage Pay business name, 2: "live" or "test mode (sandbox)" */
     539                                        echo esc_html( sprintf( __( 'This store takes payments for %1$s (%2$s) now.', 'mintage-pay' ), $current_name, $current_mode ) );
     540                                } else {
     541                                        /* translators: %s: "live" or "test mode (sandbox)" */
     542                                        echo esc_html( sprintf( __( 'This store has a working Mintage Pay key now (%s).', 'mintage-pay' ), $current_mode ) );
     543                                }
     544                                echo ' ';
     545                                /* translators: %s: "live" or "test mode (sandbox)" */
     546                                echo esc_html( sprintf( __( 'You just approved a new connection (%s). Replacing the key sends new payments to the account you approved.', 'mintage-pay' ), $new_mode ) );
     547                                ?>
     548                        </p>
     549                        <?php if ( Mintage_Pay::SANDBOX_API_BASE === $pending['base'] && ! Mintage_Pay::is_test_key( (string) $this->get_option( 'api_key' ) ) ) : ?>
     550                                <p><strong style="color:#b32d2e;"><?php esc_html_e( 'The new key is a test key: customers would pay with test money and no real payment would reach you.', 'mintage-pay' ); ?></strong></p>
     551                        <?php endif; ?>
     552                        <p>
     553                                <a class="button button-primary" href="<?php echo esc_url( wp_nonce_url( admin_url( 'admin-post.php?action=' . Mintage_Pay_Connect::CONFIRM_ACTION ), Mintage_Pay_Connect::CONFIRM_ACTION ) ); ?>"><?php esc_html_e( 'Replace the key', 'mintage-pay' ); ?></a>
     554                                <a class="button" href="<?php echo esc_url( wp_nonce_url( admin_url( 'admin-post.php?action=' . Mintage_Pay_Connect::CANCEL_ACTION ), Mintage_Pay_Connect::CANCEL_ACTION ) ); ?>"><?php esc_html_e( 'Keep the current key', 'mintage-pay' ); ?></a>
     555                        </p>
     556                </div>
     557                <?php
     558        }
     559
     560        /**
     561         * The "Plugin updates" row: WordPress's own auto-update choice for this plugin (the Plugins page's "Enable
     562         * auto-updates" link), shown as a checkbox.
     563         *
     564         * @param string $key  Field key.
     565         * @param array  $data Field data.
     566         * @return string
     567         */
     568        public function generate_mintagepay_auto_update_html( $key, $data ) {
     569                $state     = Mintage_Pay_Connect::auto_update_state();
     570                $field_key = $this->get_field_key( $key );
     571                ob_start();
     572                ?>
     573                <tr valign="top">
     574                        <th scope="row" class="titledesc"><label for="<?php echo esc_attr( $field_key ); ?>"><?php echo esc_html( $data['title'] ); ?></label></th>
     575                        <td class="forminp">
     576                                <fieldset>
     577                                        <label for="<?php echo esc_attr( $field_key ); ?>">
     578                                                <input type="checkbox" name="<?php echo esc_attr( $field_key ); ?>" id="<?php echo esc_attr( $field_key ); ?>" value="1" <?php checked( $state['on'] ); ?> <?php disabled( ! $state['allowed'] ); ?> />
     579                                                <?php esc_html_e( 'Update Mintage Pay automatically when a new version is out', 'mintage-pay' ); ?>
     580                                        </label>
     581                                        <p class="description">
     582                                                <?php
     583                                                if ( $state['allowed'] ) {
     584                                                        esc_html_e( 'The same as "Enable auto-updates" for Mintage Pay on the Plugins page. Recommended: fixes and new payment options reach your checkout without you doing anything. If your host or a tool such as MainWP manages your updates, that may already cover this site.', 'mintage-pay' );
     585                                                } else {
     586                                                        esc_html_e( 'Your host or your site settings manage plugin updates here, or your user cannot update plugins, so this cannot be changed from this page.', 'mintage-pay' );
     587                                                }
     588                                                ?>
     589                                        </p>
     590                                </fieldset>
     591                        </td>
     592                </tr>
     593                <?php
     594                return (string) ob_get_clean();
     595        }
     596
     597        /**
     598         * The auto-update checkbox: 'yes' or 'no' (WordPress's own setting is the one that counts, see process_admin_options()).
     599         *
     600         * @param string $key   Key.
     601         * @param mixed  $value Value.
     602         * @return string
     603         */
     604        public function validate_mintagepay_auto_update_field( $key, $value ) {
     605                return ! empty( $value ) ? 'yes' : 'no';
     606        }
     607
     608        /* ------------------------------------------------------------------ *
     609         * WooCommerce's Payments page (WooCommerce 9.6+ calls these; older versions never do). The card and crypto methods
     610         * share the main method's key, so "set up", "connected" and "test mode" are the same for all three.
     611         * ------------------------------------------------------------------ */
     612
     613        /**
     614         * Needs setup until a key has been checked with Mintage Pay. Not "is usable": that also needs the method switched
     615         * on, and WooCommerce will not switch on a method that says it needs setup.
     616         *
     617         * @return bool
     618         */
     619        public function needs_setup() {
     620                return ! Mintage_Pay::is_key_verified();
     621        }
     622
     623        /**
     624         * Connected: a key is saved and Mintage Pay accepted it.
     625         *
     626         * @return bool
     627         */
     628        public function is_account_connected() {
     629                return Mintage_Pay::is_key_verified();
     630        }
     631
     632        /**
     633         * Test mode: the saved key is a sandbox key.
     634         *
     635         * @return bool
     636         */
     637        public function is_test_mode() {
     638                return Mintage_Pay::is_test_key( (string) Mintage_Pay::setting( 'api_key', '' ) );
     639        }
     640
     641        /**
     642         * This method's own settings page.
     643         *
     644         * @return string
     645         */
     646        public function get_settings_url() {
     647                return admin_url( 'admin.php?page=wc-settings&tab=checkout&section=' . $this->id );
     648        }
     649
     650        /**
     651         * Where "Complete setup" goes: the main method's settings, with the Connect buttons.
     652         *
     653         * @param string $return_url Where WooCommerce would like to come back to (not used: the settings page is the end).
     654         * @return string
     655         */
     656        public function get_connection_url( $return_url = '' ) {
     657                return Mintage_Pay::settings_url();
     658        }
     659
     660        /**
     661         * Links on the Payments page's menu for this method.
     662         *
     663         * @param string $country_code Store country (not used: the same links everywhere).
     664         * @return array[]
     665         */
     666        public function get_provider_links( $country_code = '' ) {
     667                return self::provider_links();
     668        }
     669
     670        /**
     671         * The Payments page links (pure, tested in tests/payments-page-test.php).
     672         *
     673         * @return array[]
     674         */
     675        public static function provider_links() {
     676                return array(
     677                        array(
     678                                '_type' => 'about',
     679                                'url'   => 'https://mintagepay.com/',
     680                        ),
     681                        array(
     682                                '_type' => 'documentation',
     683                                'url'   => 'https://mintagepay.com/docs/integrations/#woocommerce',
     684                        ),
     685                        array(
     686                                '_type' => 'support',
     687                                'url'   => 'https://mintagepay.com/docs/',
     688                        ),
     689                        array(
     690                                '_type' => 'pricing',
     691                                'url'   => 'https://mintagepay.com/#pricing',
     692                        ),
     693                        array(
     694                                '_type' => 'terms',
     695                                'url'   => 'https://mintagepay.com/terms.html',
     696                        ),
     697                );
     698        }
     699
     700        /**
    448701         * The webhook row stores nothing.
    449702         *
    … …  
    521774
    522775        /**
    523          * Save settings, then validate a new key with GET /v1/me and register the webhook.
     776         * Save settings, then validate a new key with GET /v1/me and register the webhook (Mintage_Pay_Admin::activate_key(),
     777         * the same steps "Connect to Mintage Pay" runs), and apply the auto-update choice.
    524778         *
    525779         * @return bool
    … …  
    536790                $this->enabled     = $this->get_option( 'enabled' );
    537791
    538                 $new_key  = (string) $this->get_option( 'api_key' );
    539                 $new_base = Mintage_Pay::api_base_for( $new_key, (string) $this->get_option( 'api_base_url' ) );
    540                 delete_transient( Mintage_Pay::RAILS_TRANSIENT );
    541                 $old_api  = '' !== $old_key ? Mintage_Pay::api( $old_key, $old_base ) : null;
    542 
    543                 if ( '' === $new_key ) {
    544                         // Key removed: disable the old endpoint and forget the secret.
    545                         $hook = Mintage_Pay::get_webhook();
    546                         if ( $hook && $old_api && isset( $hook['fingerprint'] ) && Mintage_Pay::fingerprint( $old_base, $old_key ) === $hook['fingerprint'] ) {
    547                                 $old_api->delete_webhook( $hook['id'] );
    548                         }
    549                         Mintage_Pay::clear_webhook();
    550                         Mintage_Pay::clear_key_verified();
    551                         return $saved;
    552                 }
    553 
    554                 $credentials_changed = ( $new_key !== $old_key || $new_base !== $old_base );
    555                 $hook                = Mintage_Pay::get_webhook();
    556                 $fingerprint         = Mintage_Pay::fingerprint( $new_base, $new_key );
    557                 $hook_ok             = $hook && ! empty( $hook['secret'] ) && isset( $hook['fingerprint'] ) && $hook['fingerprint'] === $fingerprint;
    558 
    559                 if ( ! $credentials_changed && $hook_ok && Mintage_Pay::is_key_verified() ) {
    560                         return $saved;
    561                 }
    562 
    563                 $api = Mintage_Pay::api( $new_key, $new_base );
    564                 $me  = $api->get_me();
    565                 if ( is_wp_error( $me ) ) {
    566                         // A new key is unverified by definition (the stored flag holds another key's fingerprint).
    567                         Mintage_Pay_Admin::handle_me_error( $me );
    568                         Mintage_Pay::add_notice(
    569                                 'error',
    570                                 sprintf(
    571                                         /* translators: %s: error message */
    572                                         __( 'Mintage Pay: the API key could not be verified (%s). The payment method stays hidden at checkout until the key is verified. Check the key and save again.', 'mintage-pay' ),
    573                                         Mintage_Pay_Admin::me_error_text( $me, $new_key, (string) $this->get_option( 'api_base_url' ) )
    574                                 )
    575                         );
    576                         return $saved;
    577                 }
    578 
    579                 $merchant_id = Mintage_Pay_Admin::merchant_id( $me );
    580                 $name        = isset( $me['merchant']['name'] ) && is_string( $me['merchant']['name'] ) ? $me['merchant']['name'] : '';
    581                 Mintage_Pay::mark_key_verified( $api->fingerprint(), $merchant_id, $name );
    582                 Mintage_Pay::cache_rails( $me );
    583 
    584                 if ( $hook_ok ) {
    585                         Mintage_Pay::add_notice( 'success', $this->connected_message( $name ) );
    586                         return $saved;
    587                 }
    588 
    589                 $previous_api = null;
    590                 if ( $hook && isset( $hook['fingerprint'] ) ) {
    591                         if ( $old_api && Mintage_Pay::fingerprint( $old_base, $old_key ) === $hook['fingerprint'] ) {
    592                                 $previous_api = $old_api;
    593                         } elseif ( $hook['fingerprint'] === $fingerprint ) {
    594                                 $previous_api = $api;
    595                         }
    596                 }
    597 
    598                 $result = Mintage_Pay_Admin::connect_webhook( $api, $previous_api, $merchant_id );
    599                 if ( true === $result ) {
    600                         Mintage_Pay::add_notice( 'success', $this->connected_message( $name ) . ' ' . __( 'Webhook connected.', 'mintage-pay' ) );
    601                 }
     792                Mintage_Pay_Admin::activate_key( $old_key, $old_base, (string) $this->get_option( 'api_key' ), (string) $this->get_option( 'api_base_url' ) );
     793                Mintage_Pay_Connect::save_auto_update( 'yes' === $this->get_option( 'auto_update' ) );
    602794                return $saved;
    603         }
    604 
    605         /**
    606          * "Connected to Mintage Pay as X."
    607          *
    608          * @param string $name Merchant name.
    609          * @return string
    610          */
    611         private function connected_message( $name ) {
    612                 $mode = Mintage_Pay::is_test_key( (string) $this->get_option( 'api_key' ) )
    613                         ? __( 'TEST MODE: sandbox, test money only', 'mintage-pay' )
    614                         : __( 'live', 'mintage-pay' );
    615                 return '' !== $name
    616                         /* translators: 1: merchant business name, 2: "live" or "TEST MODE: ..." */
    617                         ? sprintf( __( 'Mintage Pay: API key verified for %1$s (%2$s).', 'mintage-pay' ), $name, $mode )
    618                         /* translators: %s: "live" or "TEST MODE: ..." */
    619                         : sprintf( __( 'Mintage Pay: API key verified (%s).', 'mintage-pay' ), $mode );
    620795        }
    621796
  • mintage-pay/trunk/mintage-pay.php

    r3768248 r3768791  
    44 * Plugin URI:           https://mintagepay.com/docs/integrations/#woocommerce
    55 * Description:          Accept USDC and USDT stablecoin payments through the Mintage Pay hosted checkout, with prices in USD, EUR, GBP and more.
    6  * Version:              1.17.0
     6 * Version:              1.18.0
    77 * Requires at least:    6.4
    88 * Requires PHP:         7.4
    … …  
    1414 * Text Domain:          mintage-pay
    1515 * WC requires at least: 8.0
    16  * WC tested up to:      11.1
     16 * WC tested up to:      11.2
    1717 *
    1818 * @package MintagePay
    … …  
    2121defined( 'ABSPATH' ) || exit;
    2222
    23 define( 'MINTAGE_PAY_VERSION', '1.17.0' );
     23define( 'MINTAGE_PAY_VERSION', '1.18.0' );
    2424define( 'MINTAGE_PAY_FILE', __FILE__ );
    2525define( 'MINTAGE_PAY_PATH', plugin_dir_path( __FILE__ ) );
    … …  
    153153                require_once MINTAGE_PAY_PATH . 'includes/class-mintagepay-reminder.php';
    154154                require_once MINTAGE_PAY_PATH . 'includes/class-mintagepay-two-rail.php';
     155                require_once MINTAGE_PAY_PATH . 'includes/class-mintagepay-connect.php';
    155156
    156157                add_filter( 'woocommerce_payment_gateways', array( __CLASS__, 'add_gateway' ) );
    … …  
    176177                Mintage_Pay_Discount::init();
    177178                Mintage_Pay_Reminder::init();
     179                Mintage_Pay_Connect::init();
    178180        }
    179181
  • mintage-pay/trunk/readme.txt

    r3768368 r3768791  
    77WC requires at least: 8.0
    88WC tested up to: 11.2
    9 Stable tag: 1.17.0
     9Stable tag: 1.18.0
    1010License: GPLv2 or later
    1111License URI: https://www.gnu.org/licenses/gpl-2.0.html
    … …  
    2121* **Pay by app**: Cash App in one tap, plus Apple Pay and Google Pay where Mintage Pay offers them for the total.
    2222
    23 **Get started in minutes:** install, try it with test money in the sandbox, then apply for a live account. See Installation below, or the full guide at https://mintagepay.com/docs/integrations/#woocommerce
     23**Get started in minutes:** install, click "Connect to Mintage Pay" and approve your store. No key to copy. Try it with test money in the sandbox first if you like, then apply for a live account. See Installation below, or the full guide at https://mintagepay.com/docs/integrations/#woocommerce
    2424
    2525Mintage Pay sends your customer to a secure hosted checkout where they pay in stablecoins (USDC or USDT) from a wallet or an exchange, on the networks you turn on in your Mintage Pay dashboard (for example Base, Polygon, Solana or TRON). A network appears at checkout only when you have set a payout address for it.
    … …  
    5353What is sent, and when:
    5454
     55* When you click "Connect to Mintage Pay" (or "Connect a test (sandbox) account"): your browser opens app.mintagepay.com (or sandbox.mintagepay.com) with this site's address, its name, the plugin version, a random value and a code challenge (PKCE). You sign in there and approve this store. Your browser then comes back to this site with a one-time code, and the plugin sends that code and its secret to Mintage Pay to receive this store's API key. Nothing is sent before you click.
    5556* When you save the settings, reconnect the webhook, or activate, deactivate or delete the plugin: your API key (in the Authorization header), this store's webhook URL and the store name, to check the key and to add or remove the webhook endpoint.
    5657* When a customer chooses Mintage Pay at checkout: the order total and currency, the order number, a description ("Order #1042 from Your Store"), the thank-you and cancel page addresses, the WooCommerce order id and order key, the invoice expiry, and the customer's billing email address. Mintage Pay emails the customer a receipt only if you turn off "Let WooCommerce send the order emails"; otherwise it writes to them only when they ask for a refund on Mintage Pay's refund page. For an order paid by card, the email is also kept as part of the record of that card payment, in case the customer disputes the charge.
    … …  
    7475**3. Go live.** Apply at https://mintagepay.com/#apply. Once your account is approved, sign in at https://app.mintagepay.com, set a payout address for each network you want to be paid on, and create a live key (mp_live_...).
    7576
    76 **4. Connect your store.** Go to WooCommerce > Settings > Payments > Mintage Pay, paste the key, tick Enable and save. Saving checks the key and registers this store's webhook automatically. The settings page shows "Connected to (your business name) (live)" when it is done, or "(TEST MODE: sandbox, test money only)" for a sandbox key.
    77 
    78 To try it first, use a sandbox key (mp_test_...) from https://sandbox.mintagepay.com. Leave "API base URL" empty: the key decides whether the plugin talks to the sandbox or the live service. Replace it with a live key (mp_live_...) before you take real orders.
     77**4. Connect your store.** Go to WooCommerce > Settings > Payments > Mintage Pay and click "Connect to Mintage Pay" (or "Connect a test (sandbox) account"). Sign in to Mintage Pay, check that the store address shown is yours, and click Approve. You come back to WordPress with the key saved, the webhook registered and the payment method switched on. You can also paste a key from Developers > API keys instead: tick Enable and save. Saving checks the key and registers this store's webhook automatically. The settings page shows "Connected to (your business name) (live)" when it is done, or "(TEST MODE: sandbox, test money only)" for a sandbox key.
     78
     79To try it first, connect a sandbox account, or paste a sandbox key (mp_test_...) from https://sandbox.mintagepay.com. Click "Connect to Mintage Pay" again later to switch to your live account: the plugin asks before it replaces a working key. Leave "API base URL" empty: the key decides whether the plugin talks to the sandbox or the live service. Replace it with a live key (mp_live_...) before you take real orders.
    7980
    8081If you move the store to a new address or the webhook shows "Not connected", click "Reconnect webhook" on the settings page.
    … …  
    8283**5. Choose your methods.** Under WooCommerce > Settings > Payments, switch on "Mintage Pay: Pay by card" and "Mintage Pay: Pay with crypto" if you want them as their own methods, and drag the methods into the order you want.
    8384
    84 **6. Stay up to date automatically.** On the Plugins screen, click "Enable auto-updates" next to Mintage Pay. Fixes and new payment options then install on their own.
     85**6. Stay up to date automatically.** Tick "Update Mintage Pay automatically" on the settings page, or click "Enable auto-updates" next to Mintage Pay on the Plugins screen (it is the same setting). Fixes and new payment options then install on their own.
    8586
    8687= Documentation =
    … …  
    100101Yes. A sandbox account for testing is free and instant at https://sandbox.mintagepay.com. A live account is by application at https://mintagepay.com/#apply.
    101102
     103= What does "Connect to Mintage Pay" do? =
     104
     105It makes an API key for this store in your Mintage Pay account and saves it here, so you never copy a key by hand. You approve it on Mintage Pay's own site, signed in to your account, and the page shows the store address that asked. Only approve if you just clicked Connect in your own WordPress. The key appears under Developers > API keys as "WooCommerce (your store address)" and the account owner gets an email. If you connect the same store again, the key from the earlier Connect is revoked as soon as the new one is used. Keys you made by hand are never touched.
     106
    102107= Why don't I see Pay by card or Pay by app? =
    103108
    … …  
    151156
    152157== Changelog ==
     158
     159= 1.18.0 =
     160* New: "Connect to Mintage Pay". Click it on the settings page, sign in to Mintage Pay and approve your store: the API key is saved, the webhook is registered and the payment method is switched on. No key to copy. "Connect a test (sandbox) account" does the same with test money. If a working key is already saved, the plugin asks before replacing it.
     161* New: a "Get started" box on the settings page while no key is saved, with Connect, the sandbox, the application form and the setup guide.
     162* New: "Update Mintage Pay automatically" on the settings page (WordPress's own auto-update setting for this plugin).
     163* WooCommerce's Payments page now shows the Mintage Pay icon, says the method needs setup until a key is verified and shows test mode for a sandbox key, and links to the guide, pricing and terms. Each Mintage Pay method opens its own settings.
     164* Fix: with only the main method on, its row showed the Cash App, Apple Pay and Google Pay icons even where Mintage Pay does not offer those apps for the store. The icons now follow what Mintage Pay offers, on the classic and the block checkout.
     165* Tested with WooCommerce 11.2.
    153166
    154167= 1.17.0 =
    … …  
    269282= 1.0.0 =
    270283* First release.
     284
     285== Upgrade Notice ==
     286
     287= 1.18.0 =
     288One-click "Connect to Mintage Pay" (no key to copy), a Get started box, an "Update Mintage Pay automatically" setting, and a fix for app icons shown where the apps are not offered.
Note: See TracChangeset for help on using the changeset viewer.