$id ? $id : 'inv_' . wp_generate_password( 10, false ), 'slug' => '', 'type' => $type, 'title' => isset( $data['title'] ) ? sanitize_text_field( $data['title'] ) : __( 'Payment', 'phemightforms' ), 'description' => isset( $data['description'] ) ? sanitize_textarea_field( $data['description'] ) : '', 'amount' => Payments::to_amount( isset( $data['amount'] ) ? $data['amount'] : 0 ), 'customer_name' => isset( $data['customer_name'] ) ? sanitize_text_field( $data['customer_name'] ) : '', 'customer_email' => isset( $data['customer_email'] ) ? sanitize_email( $data['customer_email'] ) : '', 'payment_methods' => $methods, 'payment_method' => $methods ? $methods[0] : 'offline', 'due_date' => isset( $data['due_date'] ) ? sanitize_text_field( $data['due_date'] ) : '', 'line_items' => isset( $data['line_items'] ) ? sanitize_textarea_field( $data['line_items'] ) : '', 'status' => isset( $data['status'] ) ? sanitize_key( $data['status'] ) : 'pending', 'txn_id' => '', 'created' => current_time( 'mysql' ), 'updated' => current_time( 'mysql' ), ]; $found = false; $items = (array) get_option( self::OPTION, [] ); foreach ( $items as &$item ) { if ( isset( $item['id'] ) && $item['id'] === $row['id'] ) { $row['slug'] = $item['slug'] ?? self::make_slug( $row['title'], $row['id'] ); $row['created'] = $item['created'] ?? $row['created']; $row['txn_id'] = $item['txn_id'] ?? ''; if ( 'paid' === ( $item['status'] ?? '' ) ) { $row['status'] = 'paid'; } $item = $row; $found = true; break; } } unset( $item ); if ( ! $found ) { $row['slug'] = self::make_slug( $row['title'], $row['id'] ); $items[] = $row; } update_option( self::OPTION, $items ); return $row['id']; } /** * Delete an invoice. * * @param string $id Invoice ID. */ public static function delete( $id ) { $items = array_filter( self::all(), static function ( $item ) use ( $id ) { return ! isset( $item['id'] ) || $item['id'] !== $id; } ); update_option( self::OPTION, array_values( $items ) ); } /** * Public pay URL for an invoice slug. * * @param string $slug URL slug. * @return string */ public static function url( $slug ) { return home_url( 'phemight-pay/' . rawurlencode( sanitize_title( $slug ) ) . '/' ); } /** * Public PDF download URL for an invoice slug. * * @param string $slug URL slug. * @return string */ public static function pdf_url( $slug ) { return home_url( 'phemight-pay/' . rawurlencode( sanitize_title( $slug ) ) . '/pdf/' ); } /** * Enabled payment methods allowed for this invoice. * * @param array $invoice Invoice row. * @return array slug => label */ public static function payment_methods_for( array $invoice ) { $enabled = Payments::enabled_providers(); $stored = []; if ( ! empty( $invoice['payment_methods'] ) && is_array( $invoice['payment_methods'] ) ) { $stored = array_map( 'sanitize_key', $invoice['payment_methods'] ); } elseif ( ! empty( $invoice['payment_method'] ) ) { // Legacy invoices saved before multi-gateway support: offer every enabled gateway. return $enabled; } $methods = []; foreach ( $stored as $slug ) { if ( isset( $enabled[ $slug ] ) ) { $methods[ $slug ] = $enabled[ $slug ]; } } return $methods ? $methods : $enabled; } /** * Normalize payment method selection from save payload. * * @param array $data Save payload. * @param array $enabled Enabled providers. * @return string[] */ private static function sanitize_payment_methods( array $data, array $enabled ) { $keys = array_keys( $enabled ); if ( empty( $keys ) ) { return [ 'offline' ]; } if ( ! empty( $data['payment_methods_all'] ) ) { return $keys; } $raw = isset( $data['payment_methods'] ) ? (array) $data['payment_methods'] : []; $raw = array_map( 'sanitize_key', $raw ); $raw = array_values( array_intersect( $raw, $keys ) ); return $raw ? $raw : $keys; } /** * Mark invoice paid when its transaction completes. * * @param string $txn_id Transaction ID. */ public static function mark_paid_by_txn( $txn_id ) { if ( ! $txn_id ) { return; } $items = (array) get_option( self::OPTION, [] ); $dirty = false; foreach ( $items as &$item ) { if ( isset( $item['txn_id'] ) && $item['txn_id'] === $txn_id ) { $item['status'] = 'paid'; $item['updated'] = current_time( 'mysql' ); $dirty = true; } } unset( $item ); if ( $dirty ) { update_option( self::OPTION, $items ); } } /** * Process a pay-now submission and redirect or show instructions. * * @param array $invoice Invoice row. * @return array{message:string,redirect?:string} */ public static function process_payment( array $invoice, $method = '' ) { if ( 'paid' === ( $invoice['status'] ?? '' ) ) { return [ 'message' => __( 'This invoice has already been paid. Thank you!', 'phemightforms' ) ]; } $allowed = self::payment_methods_for( $invoice ); $method = $method ? sanitize_key( $method ) : ''; if ( ! $method || ! isset( $allowed[ $method ] ) ) { return [ 'message' => __( 'Please select a valid payment method.', 'phemightforms' ) ]; } $amount = (float) ( $invoice['amount'] ?? 0 ); $email = $invoice['customer_email'] ?? ''; if ( $amount <= 0 ) { return [ 'message' => __( 'Invalid payment amount.', 'phemightforms' ) ]; } $txn_id = Payments::record_transaction( [ 'form_id' => 0, 'amount' => $amount, 'method' => $method, 'status' => 'pending', 'currency' => Payments::currency(), ] ); self::attach_txn( $invoice['id'], $txn_id ); $form = new Form(); $form->title = $invoice['title']; if ( ! in_array( $method, [ 'offline', 'cryptowallet' ], true ) ) { $url = Payments::checkout_url( $form, $method, $amount, $txn_id, $email ); if ( $url ) { return [ 'message' => __( 'Redirecting you to secure checkout...', 'phemightforms' ), 'redirect' => $url, ]; } } $settings = Payments::settings(); if ( 'cryptowallet' === $method ) { return [ 'message' => sprintf( /* translators: 1: total, 2: note, 3: addresses */ __( "Thanks! Your total is %1\$s.\n%2\$s\n\n%3\$s", 'phemightforms' ), Payments::format( $amount ), trim( (string) $settings['cryptowallet_note'] ), trim( (string) $settings['cryptowallet_addresses'] ) ), ]; } return [ 'message' => sprintf( /* translators: 1: total, 2: instructions */ __( 'Thanks! Your total is %1$s. %2$s', 'phemightforms' ), Payments::format( $amount ), trim( (string) $settings['offline_instructions'] ) ), ]; } /** * Store the transaction ID on the invoice row. * * @param string $id Invoice ID. * @param string $txn_id Transaction ID. */ private static function attach_txn( $id, $txn_id ) { $items = (array) get_option( self::OPTION, [] ); foreach ( $items as &$item ) { if ( isset( $item['id'] ) && $item['id'] === $id ) { $item['txn_id'] = $txn_id; $item['status'] = 'sent'; $item['updated'] = current_time( 'mysql' ); } } unset( $item ); update_option( self::OPTION, $items ); } /** * Generate a unique URL slug. * * @param string $title Title. * @param string $id Invoice ID. * @return string */ private static function make_slug( $title, $id ) { $base = sanitize_title( $title ); if ( '' === $base ) { $base = 'pay'; } $slug = $base; $i = 1; while ( self::get_by_slug( $slug ) && ( self::get_by_slug( $slug )['id'] ?? '' ) !== $id ) { $slug = $base . '-' . $i; $i++; } return $slug; } /** * Render the public pay page. */ public function render_pay_page() { $slug = get_query_var( self::QUERY_VAR ); if ( ! $slug ) { return; } $invoice = self::get_by_slug( $slug ); if ( ! $invoice ) { status_header( 404 ); exit; } if ( get_query_var( self::PDF_VAR ) ) { InvoicePdf::download( $invoice ); } $notice = ''; $success = false; if ( 'POST' === $_SERVER['REQUEST_METHOD'] && isset( $_POST['phf_pay_nonce'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing if ( wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['phf_pay_nonce'] ) ), 'phf_pay_' . $invoice['id'] ) ) { $method = isset( $_POST['phf_payment_method'] ) ? sanitize_key( wp_unslash( $_POST['phf_payment_method'] ) ) : ''; $result = self::process_payment( $invoice, $method ); if ( ! empty( $result['redirect'] ) ) { wp_safe_redirect( $result['redirect'] ); exit; } $notice = $result['message']; $success = true; $invoice = self::get( $invoice['id'] ) ?? $invoice; } } wp_enqueue_style( 'phemightforms' ); status_header( 200 ); nocache_headers(); $is_invoice = 'invoice' === ( $invoice['type'] ?? 'invoice' ); $is_paid = 'paid' === ( $invoice['status'] ?? '' ); $methods = self::payment_methods_for( $invoice ); $pdf_url = self::pdf_url( $invoice['slug'] ); $selected = isset( $_POST['phf_payment_method'] ) ? sanitize_key( wp_unslash( $_POST['phf_payment_method'] ) ) : (string) array_key_first( $methods ); // phpcs:ignore WordPress.Security.NonceVerification.Missing if ( ! isset( $methods[ $selected ] ) ) { $selected = (string) array_key_first( $methods ); } ?> > <?php echo esc_html( $invoice['title'] ); ?> ยท <?php bloginfo( 'name' ); ?>

1 ) : ?>

$label ) : ?>