service_name() ); } /** * Optional link that opens the payment app with amount and memo filled in. * * @param WC_Order $order Order. * @return string Empty when the service has no such link. */ protected function pay_link( $order ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found return ''; } /** * Clean a handle before it is shown or used in a link. * * @param string $handle Raw handle. * @return string */ protected function clean_handle( $handle ) { return trim( (string) $handle ); } /** * Common constructor. */ public function __construct() { $this->has_fields = false; $this->supports = array( 'products' ); $this->init_form_fields(); $this->init_settings(); $this->title = $this->get_option( 'title' ); $this->description = $this->get_option( 'description' ); $this->payee_name = $this->get_option( 'payee_name' ); $this->payee_handle = $this->clean_handle( $this->get_option( 'payee_handle' ) ); $this->qr_image = $this->get_option( 'qr_image' ); $this->instructions = $this->get_option( 'instructions' ); $this->reference_prefix = $this->get_option( 'reference_prefix', 'MM' ); if ( ! defined( 'MMP_VERSION' ) && is_admin() ) { $this->method_description .= ' ' . esc_html__( 'Want payments confirmed automatically from your bank emails? Get MemoMatch Pro.', 'memomatch' ) . ''; } add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ) ); add_action( 'woocommerce_thankyou_' . $this->id, array( $this, 'thankyou_page' ) ); add_action( 'woocommerce_email_before_order_table', array( $this, 'email_instructions' ), 10, 3 ); } /** * Settings fields. */ public function init_form_fields() { $service = $this->service_name(); $this->form_fields = array( 'enabled' => array( 'title' => __( 'Enable', 'memomatch' ), 'type' => 'checkbox', /* translators: %s: payment service name. */ 'label' => sprintf( __( 'Accept %s payments', 'memomatch' ), $service ), 'default' => 'no', ), 'title' => array( 'title' => __( 'Title', 'memomatch' ), 'type' => 'safe_text', 'description' => __( 'What shoppers see at checkout.', 'memomatch' ), /* translators: %s: payment service name. */ 'default' => sprintf( __( 'Pay with %s', 'memomatch' ), $service ), 'desc_tip' => true, ), 'description' => array( 'title' => __( 'Description', 'memomatch' ), 'type' => 'textarea', 'description' => __( 'Shown under the title at checkout.', 'memomatch' ), /* translators: %s: payment service name. */ 'default' => sprintf( __( 'Place your order, then send the total with %s. You will get the payment details and your reference code on the next page.', 'memomatch' ), $service ), 'desc_tip' => true, ), 'payee_name' => array( 'title' => __( 'Recipient name', 'memomatch' ), 'type' => 'safe_text', 'description' => __( 'The name shoppers will see in their app when they send money, so they know they are paying the right person.', 'memomatch' ), 'default' => get_bloginfo( 'name' ), 'desc_tip' => true, ), 'payee_handle' => array( 'title' => $this->handle_label(), 'type' => 'safe_text', 'description' => $this->handle_help(), 'default' => '', ), 'qr_image' => array( 'title' => __( 'QR code image URL (optional)', 'memomatch' ), 'type' => 'url', 'description' => __( 'Upload your QR code from your banking or payment app to the Media Library and paste its URL here.', 'memomatch' ), 'default' => '', ), 'instructions' => array( 'title' => __( 'Extra instructions', 'memomatch' ), 'type' => 'textarea', 'description' => __( 'Added to the thank you page and the order email.', 'memomatch' ), 'default' => __( 'We ship your order as soon as your payment arrives.', 'memomatch' ), 'desc_tip' => true, ), 'reference_prefix' => array( 'title' => __( 'Reference prefix', 'memomatch' ), 'type' => 'safe_text', 'description' => __( 'Letters added to the start of every reference code, for example MM gives MM-1043-K7QD.', 'memomatch' ), 'default' => 'MM', 'desc_tip' => true, ), ); } /** * Only offer the method when a handle is set and the store charges in US dollars. * * @return bool */ public function is_available() { if ( '' === $this->payee_handle ) { return false; } $currencies = apply_filters( 'memomatch_supported_currencies', array( 'USD' ), $this->id ); if ( ! in_array( get_woocommerce_currency(), $currencies, true ) ) { return false; } return parent::is_available(); } /** * Build a short reference that is easy to type into a memo field. * * @param WC_Order $order Order. * @return string */ protected function generate_reference( $order ) { $alphabet = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'; $suffix = ''; for ( $i = 0; $i < 4; $i++ ) { $suffix .= $alphabet[ wp_rand( 0, strlen( $alphabet ) - 1 ) ]; } $prefix = strtoupper( preg_replace( '/[^A-Za-z0-9]/', '', (string) $this->reference_prefix ) ); $parts = array_filter( array( $prefix, $order->get_order_number(), $suffix ), 'strlen' ); return apply_filters( 'memomatch_reference', implode( '-', $parts ), $order, $this ); } /** * Get or create the reference for an order. * * @param WC_Order $order Order. * @return string */ public function get_reference( $order ) { $reference = $order->get_meta( self::META_REFERENCE ); if ( ! $reference ) { $reference = $this->generate_reference( $order ); $order->update_meta_data( self::META_REFERENCE, $reference ); $order->save(); } return $reference; } /** * Place the order on hold and send the shopper to the thank you page. * * @param int $order_id Order ID. * @return array */ public function process_payment( $order_id ) { $order = wc_get_order( $order_id ); if ( ! $order ) { return array( 'result' => 'failure' ); } $reference = $this->get_reference( $order ); if ( $order->get_total() > 0 ) { $order->update_status( apply_filters( 'memomatch_initial_order_status', 'on-hold', $order, $this ), /* translators: 1: payment service name, 2: reference code. */ sprintf( __( 'Waiting for %1$s payment. Reference: %2$s', 'memomatch' ), $this->service_name(), $reference ) ); } else { $order->payment_complete(); } wc_maybe_reduce_stock_levels( $order_id ); WC()->cart->empty_cart(); return array( 'result' => 'success', 'redirect' => $this->get_return_url( $order ), ); } /** * Thank you page box. * * @param int $order_id Order ID. */ public function thankyou_page( $order_id ) { $order = wc_get_order( $order_id ); if ( ! $order || ! $order->has_status( array( 'on-hold', 'pending' ) ) ) { return; } echo $this->render_instructions( $order, false ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- escaped in render_instructions(). } /** * Add payment details to the on hold email. * * @param WC_Order $order Order. * @param bool $sent_to_admin Whether the email goes to the store. * @param bool $plain_text Plain text email. */ public function email_instructions( $order, $sent_to_admin, $plain_text = false ) { if ( $sent_to_admin || ! $order instanceof WC_Order || $this->id !== $order->get_payment_method() || ! $order->has_status( 'on-hold' ) ) { return; } if ( $plain_text ) { echo esc_html( $this->render_instructions_plain( $order ) ) . "\n"; $link = $this->pay_link( $order ); if ( $link ) { echo "\n" . esc_url_raw( $link ) . "\n"; } echo "\n"; return; } echo $this->render_instructions( $order, true ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- escaped in render_instructions(). } /** * Plain text payment details for plain text emails. * * @param WC_Order $order Order. * @return string */ public function render_instructions_plain( $order ) { $amount = html_entity_decode( wp_strip_all_tags( wc_price( $order->get_total(), array( 'currency' => $order->get_currency() ) ) ), ENT_QUOTES, 'UTF-8' ); $to = $this->payee_handle . ( $this->payee_name ? ' (' . $this->payee_name . ')' : '' ); /* translators: %s: payment service name. */ $lines = array( strtoupper( sprintf( __( 'How to pay with %s', 'memomatch' ), $this->service_name() ) ), '' ); $lines[] = '1. ' . $this->open_app_text(); $lines[] = '2. ' . __( 'Send to:', 'memomatch' ) . ' ' . $to; $lines[] = '3. ' . __( 'Amount:', 'memomatch' ) . ' ' . $amount; $lines[] = '4. ' . __( 'Write this code in the memo or note:', 'memomatch' ) . ' ' . $this->get_reference( $order ); if ( $this->instructions ) { $lines[] = ''; $lines[] = $this->instructions; } return implode( "\n", $lines ); } /** * HTML for the payment details. All values are escaped here. * * @param WC_Order $order Order. * @param bool $is_email Whether this is for an email. * @return string */ public function render_instructions( $order, $is_email ) { $reference = $this->get_reference( $order ); $amount = wp_strip_all_tags( wc_price( $order->get_total(), array( 'currency' => $order->get_currency() ) ) ); $link = $this->pay_link( $order ); $box_style = $is_email ? 'border:1px solid #e5e5e5;padding:16px;margin:0 0 24px;' : ''; ob_start(); ?>

service_name() ) ); ?>

  1. open_app_text() ); ?>
  2. payee_handle ); ?> payee_name ) : ?> (payee_name ); ?>)

service_name() ) ); ?>

qr_image ) : ?>

<?php esc_attr_e( 'Payment QR code', 'memomatch' ); ?>

instructions ) : ?>

instructions ) ) ); ?>