• Resolved robelo2

    (@robelo2)


    So I am trying to make a plugin that adds another gateway for NowPayments payment with xmr (monero) cryptocurrency.

    https://imgur.com/a/yttNviK some screens from the menus for the plugin. The problem is when I am trying to checkout I get no redirection to the nowpayments site and I get to the confirmation page saying I paid with 0 dollars and the payment method is blank. I get no specific errors either so I definitely need help to get this plugin working. Maybe you can help me?

    Here is my code for the ipn handler and I know this works cause I have tested it with postman app and with a curl command.

    <?php
    // Enable error reporting for debugging
    error_reporting(E_ALL);
    ini_set('display_errors', 1);
    ini_set('log_errors', 1);
    ini_set('error_log', '/path/to/php-error.log'); // Update this path

    error_log('IPN Handler Started');

    // Retrieve the secret key
    $secret = 'your-secret-key'; // Ensure this is correct

    // Get the raw POST data
    $payload = file_get_contents('php://input');
    error_log('Received Payload: ' . $payload);

    // Get the signature from the headers
    $signature = isset($_SERVER['HTTP_X_NOWPAYMENTS_SIG']) ? $_SERVER['HTTP_X_NOWPAYMENTS_SIG'] : '';
    error_log('Received Signature: ' . $signature);

    // Calculate the expected signature
    $calculated_signature = hash_hmac('sha512', $payload, $secret);
    error_log('Calculated Signature: ' . $calculated_signature);

    // Verify the signature
    if ($calculated_signature === $signature) {
    error_log('Signature verified successfully.');

    // Decode the payload
    $data = json_decode($payload, true);
    if ($data === null) {
    error_log('JSON Decode Error: ' . json_last_error_msg());
    } else {
    error_log('Decoded Data: ' . print_r($data, true));

    // Process the payment status
    if (isset($data['status']) && $data['status'] === 'paid') {
    error_log('Payment confirmed for order: ' . $data['order_id']);
    // Handle the payment logic here
    } else {
    error_log('Invalid status: ' . $data['status']);
    }
    }
    } else {
    error_log('Signature verification failed');
    error_log('Received Signature: ' . $signature);
    error_log('Calculated Signature: ' . $calculated_signature);
    }

    // Send response
    http_response_code(200);
    exit();
    ?>

    and here is the main plugin code that probably needs some revising:

    <?php
    /*
    Plugin Name: Paid Memberships Pro - Add NowPayments
    Plugin URI: https://example.com
    Description: Add NowPayments as a Payment Option for Monero and other cryptocurrencies.
    Version: 0.2
    Author: Your Name
    Author URI: https://example.com
    Text Domain: pmpro-add-nowpayments
    Domain Path: /languages
    */

    // Load plugin text domain
    function pmpro_add_nowpayments_i18n() {
    load_plugin_textdomain('pmpro-add-nowpayments', false, basename(dirname(__FILE__)) . '/languages');
    }
    add_action('plugins_loaded', 'pmpro_add_nowpayments_i18n', 10);

    // Register plugin styles
    function pmpro_add_nowpayments_register_styles() {
    wp_register_style('pmpro-add-nowpayments-styles', plugins_url('css/pmpro-add-nowpayments.css', __FILE__));
    wp_enqueue_style('pmpro-add-nowpayments-styles');
    }
    add_action('wp_enqueue_scripts', 'pmpro_add_nowpayments_register_styles');

    // Add NowPayments to gateways
    function pmproaddnowpayments_pmpro_gateways($gateways) {
    $gateways['nowpayments'] = __('NowPayments (Cryptocurrency)', 'pmpro-add-nowpayments');
    return $gateways;
    }


    add_filter('pmpro_gateways', 'pmproaddnowpayments_pmpro_gateways');

    add_action('pmpro_checkout_before_payment_information_fields', function() {
    global $pmpro_requirebilling;
    });


    // Log the current billing requirement at checkout
    function log_billing_requirement_in_checkout() {
    global $pmpro_requirebilling;
    error_log('Billing requirement during checkout (before filter): ' . (isset($pmpro_requirebilling) ? $pmpro_requirebilling : 'Not set'));
    }
    add_action('pmpro_checkout_boxes', 'log_billing_requirement_in_checkout');

    // Log billing requirement after filter is applied
    function log_billing_requirement($require_billing) {
    return $require_billing;
    }
    add_filter('pmpro_require_billing', 'log_billing_requirement', 15);

    function pmproaddnowpayments_disable_billing($require_billing) {
    global $gateway, $pmpro_requirebilling;

    if ($gateway === 'nowpayments') {
    $pmpro_requirebilling = false; // Force it globally here
    error_log('Billing requirement set to false for NowPayments.');
    return false;
    }

    return $require_billing;
    }

    add_filter('pmpro_require_billing', 'pmproaddnowpayments_disable_billing', 50);

    function pmproaddnowpayments_set_gateway() {
    global $gateway;

    // Explicitly retrieve the selected gateway in case it's not set
    if (!isset($gateway) || empty($gateway)) {
    $gateway = pmpro_getOption("gateway"); // Or another way to retrieve the selected gateway
    error_log('Gateway not previously set, retrieved: ' . $gateway);
    error_log('Price passed to NowPayments: ' . $amount_to_pay);
    error_log('NowPayments data sent: ' . print_r($nowpayments_request, true));

    }
    }
    add_action('pmpro_checkout_before_processing', 'pmproaddnowpayments_set_gateway');


    // Force NowPayments gateway to process payment (bypass billing requirement)
    function pmproaddnowpayments_checkout_display() {
    global $gateway, $pmpro_requirebilling;

    error_log('pmproaddnowpayments_checkout_display hook fired, gateway: ' . $gateway);

    if ($gateway === 'nowpayments') {
    // Force billing to be false here just before rendering checkout
    $pmpro_requirebilling = false;
    error_log('Billing requirement forced to false during checkout display for NowPayments.');

    ?>
    <fieldset id="pmpro_payment_method" class="<?php echo esc_attr(pmpro_get_element_class('pmpro_form_fieldset', 'pmpro_payment_method')); ?>">
    <div class="<?php echo esc_attr(pmpro_get_element_class('pmpro_card')); ?>">
    <div class="<?php echo esc_attr(pmpro_get_element_class('pmpro_card_content')); ?>">
    <legend class="<?php echo esc_attr(pmpro_get_element_class('pmpro_form_legend')); ?>">
    <h2 class="<?php echo esc_attr(pmpro_get_element_class('pmpro_form_heading pmpro_font-large')); ?>">
    <?php esc_html_e('Cryptocurrency Payment via NowPayments', 'pmpro-add-nowpayments'); ?>
    </h2>
    </legend>
    <p><?php esc_html_e('You will be redirected to NowPayments to complete the payment.', 'pmpro-add-nowpayments'); ?></p>
    </div>
    </div>
    </fieldset>
    <?php
    }
    }
    add_action('pmpro_checkout_boxes', 'pmproaddnowpayments_checkout_display', 10);


    // Process the payment with NowPayments
    function pmproaddnowpayments_process_payment($order) {
    global $gateway;
    error_log('Processing payment through NowPayments, gateway: ' . $gateway);

    if ($gateway === 'nowpayments') {
    error_log('NowPayments gateway detected, processing...');

    // Check if the order has a valid ID and total
    if (!$order->id) {
    $order->code = $order->getRandomCode(); // Generate a new code
    $order->saveOrder(); // Save the new order
    error_log('Created new order with ID: ' . $order->id);
    }

    // Validate total
    if ($order->total == 0) {
    $order->total = pmpro_calculate_membership_cost($order->membership_id);
    error_log('Calculated Membership Cost: ' . $order->total);
    }

    // Save again if necessary
    if ($order->id > 0) {
    error_log('Order is now valid and being processed. ID: ' . $order->id);

    // NowPayments API credentials
    $options = get_option('pmpro_add_nowpayments_options');
    $api_key = isset($options['api_key']) ? $options['api_key'] : '';
    $sandbox_mode = isset($options['sandbox_mode']) ? (bool) $options['sandbox_mode'] : false;
    $webhook_url = isset($options['webhook_url']) ? $options['webhook_url'] : '';
    error_log('NowPayments API Response: ' . print_r($response, true));


    if (empty($api_key)) {
    error_log('NowPayments API Key is missing.');
    return;
    }

    $endpoint = $sandbox_mode ? 'https://api-sandbox.nowpayments.io/v1/payment' : 'https://api.nowpayments.io/v1/payment';

    // Prepare the request body
    $body = array(
    'price_amount' => $order->total,
    'price_currency' => $order->currency,
    'pay_currency' => 'xmr',
    'ipn_callback_url' => $webhook_url,
    'order_id' => $order->id,
    'order_description' => 'Membership Payment',
    );

    error_log('Request Body: ' . json_encode($body));

    $response = wp_remote_post($endpoint, array(
    'body' => json_encode($body),
    'headers' => array(
    'Content-Type' => 'application/json',
    'x-api-key' => $api_key
    ),
    ));

    error_log('Response status: ' . wp_remote_retrieve_response_code($response));
    error_log('Response body: ' . wp_remote_retrieve_body($response));

    if (is_wp_error($response)) {
    error_log('Error response from NowPayments: ' . $response->get_error_message());
    return false;
    }

    $data = json_decode(wp_remote_retrieve_body($response), true);
    error_log('API Response: ' . print_r($data, true));

    if (isset($data['payment_url'])) {
    error_log('Redirecting to NowPayments: ' . $data['payment_url']);
    wp_redirect($data['payment_url']); // Redirect to NowPayments
    exit; // Ensure script stops here after redirect
    } else {
    error_log('Failed to retrieve payment URL from NowPayments.');
    return false;
    }

    } else {
    error_log('Not NowPayments gateway, skipping.');
    }
    }
    add_action('pmpro_checkout_order', 'pmproaddnowpayments_process_payment', 10);
    error_log('pmpro_checkout_order Hook Fired');
    }

    // Add IPN handler to WordPress
    add_action('init', 'pmproaddnowpayments_handle_ipn');

    function pmproaddnowpayments_handle_ipn() {
    if (isset($_GET['action']) && $_GET['action'] === 'nowpayments_ipn') {
    error_log('Received IPN handler request');
    $payload = file_get_contents('php://input');
    error_log('Raw payload: ' . $payload);
    $data = json_decode($payload, true);
    error_log('Decoded data: ' . print_r($data, true));
    // Get raw POST data
    $payload = file_get_contents('php://input');
    error_log('Raw payload: ' . $payload);

    // Check if payload is empty
    if (empty($payload)) {
    error_log('Received empty payload');
    http_response_code(400); // Bad Request
    exit();
    }

    // Decode JSON payload
    $data = json_decode($payload, true);
    if (json_last_error() !== JSON_ERROR_NONE) {
    error_log('JSON decode error: ' . json_last_error_msg());
    http_response_code(400); // Bad Request
    exit();
    }

    // Log headers for debugging
    $headers = getallheaders();
    error_log('Headers: ' . print_r($headers, true));

    // Get secret and signature from headers
    $options = get_option('pmpro_add_nowpayments_options');
    $secret = isset($options['ipn_token']) ? $options['ipn_token'] : '';
    $signature = isset($_SERVER['HTTP_X_NOWPAYMENTS_SIG']) ? $_SERVER['HTTP_X_NOWPAYMENTS_SIG'] : '';

    // Log received signature
    error_log('Received signature: ' . $signature);

    // Sort parameters and generate the expected signature
    ksort($data);
    $sorted_request_json = json_encode($data);
    $calculated_signature = hash_hmac('sha512', $sorted_request_json, $secret);
    error_log('Calculated signature: ' . $calculated_signature);

    // Verify the IPN request
    if ($calculated_signature === $signature) {
    if (isset($data['status']) && $data['status'] === 'paid') {
    $order_id = $data['order_id'];

    // Fetch order and update status
    $order = new MemberOrder($order_id);
    if ($order) {
    $order->status = 'success';
    $order->saveOrder();

    // Update membership level
    pmpro_changeMembershipLevel($order->membership_id, $order->user_id);
    error_log('Order marked as success: ' . $order_id);
    } else {
    error_log('Order not found: ' . $order_id);
    }
    } else {
    error_log('Invalid IPN status: ' . print_r($data, true));
    }
    } else {
    error_log('IPN signature verification failed');
    }

    // Send acknowledgment
    http_response_code(200);
    exit();
    }
    }

    // Add menu item for plugin settings
    function pmpro_add_nowpayments_menu() {
    add_options_page(
    'NowPayments Settings',
    'NowPayments',
    'manage_options',
    'pmpro-add-nowpayments',
    'pmpro_add_nowpayments_settings_page'
    );
    }
    add_action('admin_menu', 'pmpro_add_nowpayments_menu');

    // Display settings page
    function pmpro_add_nowpayments_settings_page() {
    ?>
    <div class="wrap">
    <h1>NowPayments Settings</h1>
    <form method="post" action="options.php">
    <?php
    settings_fields('pmpro_add_nowpayments_options');
    do_settings_sections('pmpro-add-nowpayments');
    submit_button();
    ?>
    </form>
    </div>
    <?php
    }

    // Register settings
    function pmpro_add_nowpayments_settings_init() {
    register_setting('pmpro_add_nowpayments_options', 'pmpro_add_nowpayments_options');

    add_settings_section(
    'pmpro_add_nowpayments_section',
    'NowPayments Payment Settings',
    null,
    'pmpro-add-nowpayments'
    );

    add_settings_field(
    'pmpro_add_nowpayments_api_key',
    'NowPayments API Key',
    'pmpro_add_nowpayments_api_key_field',
    'pmpro-add-nowpayments',
    'pmpro_add_nowpayments_section'
    );

    add_settings_field(
    'pmpro_add_nowpayments_ipn_token',
    'NowPayments IPN Token',
    'pmpro_add_nowpayments_ipn_token_field',
    'pmpro-add-nowpayments',
    'pmpro_add_nowpayments_section'
    );

    add_settings_field(
    'pmpro_add_nowpayments_webhook_url',
    'NowPayments Webhook URL',
    'pmpro_add_nowpayments_webhook_url_field',
    'pmpro-add-nowpayments',
    'pmpro_add_nowpayments_section'
    );
    }
    add_action('admin_init', 'pmpro_add_nowpayments_settings_init');

    // Settings fields callback functions
    function pmpro_add_nowpayments_api_key_field() {
    $options = get_option('pmpro_add_nowpayments_options');
    ?>
    <input type="text" name="pmpro_add_nowpayments_options[api_key]" value="<?php echo isset($options['api_key']) ? esc_attr($options['api_key']) : ''; ?>">
    <?php
    }

    function pmpro_add_nowpayments_ipn_token_field() {
    $options = get_option('pmpro_add_nowpayments_options');
    ?>
    <input type="text" name="pmpro_add_nowpayments_options[ipn_token]" value="<?php echo isset($options['ipn_token']) ? esc_attr($options['ipn_token']) : ''; ?>">
    <?php
    }

    function pmpro_add_nowpayments_webhook_url_field() {
    $options = get_option('pmpro_add_nowpayments_options');
    ?>
    <input type="text" name="pmpro_add_nowpayments_options[webhook_url]" value="<?php echo isset($options['webhook_url']) ? esc_attr($options['webhook_url']) : ''; ?>">
    <?php
    }

    ?>
Viewing 2 replies - 1 through 2 (of 2 total)
Viewing 2 replies - 1 through 2 (of 2 total)
  • You must be logged in to reply to this topic.