Compare commits
35 Commits
Author | SHA1 | Date |
---|---|---|
wowario | 8122cdf8ef | |
wowario | b9e5fcc96e | |
tidux | 572f04dfba | |
SerHack | 9f0a8581e6 | |
SerHack | 2e6ec376a2 | |
Cactii1 | acfdc094da | |
Cactii1 | fcedf21b30 | |
xiphon | 90e058baeb | |
xiphon | 954eb92785 | |
SerHack | b45b9625fb | |
SerHack | 009f1d0594 | |
SerHack | 25416473eb | |
SerHack | a3e10ab36f | |
SerHack | 9af67ae75d | |
SerHack | a29b4d00b6 | |
SerHack | fea43715ea | |
SerHack | 596a1e29ad | |
SerHack | 646bcd671a | |
itssteven | 5fa97a8282 | |
itssteven | 21df86e4b8 | |
itssteven | 8e7d258b5b | |
itssteven | 7e48521570 | |
itssteven | ba395d507e | |
itssteven | f7d1f9b968 | |
itssteven | 184e740452 | |
itssteven | 8009819592 | |
cryptochangements34 | 5064b95040 | |
mosu forge | ad0d9ef004 | |
cryptochangements34 | 17a379a2aa | |
cryptochangements34 | 903432ac7a | |
mosu forge | f7fa0b758e | |
mosu forge | cd06d0303b | |
SerHack | ccb387095d | |
SerHack | 53463e9111 | |
SerHack | a17af45bd8 |
4
LICENSE
4
LICENSE
|
@ -1,7 +1,7 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2018, Ryo Currency Project
|
||||
Portions Copyright (c) 2017-2018, Monero Integrations
|
||||
Copyright (c) 2017-2018, Monero Integrations
|
||||
Copyright (c) 2018, Fireice Trust Fund
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -20,6 +20,12 @@
|
|||
|
||||
## Installing the plugin
|
||||
|
||||
### Automatic Method
|
||||
|
||||
In the "Add Plugins" section of the WordPress admin UI, search for "monero" and click the Install Now button next to "Monero WooCommerce Extension" by SerHack. This will enable auto-updates, but only for official releases, so if you need to work from git master or your local fork, please use the manual method below.
|
||||
|
||||
### Manual Method
|
||||
|
||||
* Download the plugin from the [releases page](https://github.com/monero-integrations/monerowp) or clone with `git clone https://github.com/monero-integrations/monerowp`
|
||||
* Unzip or place the `monero-woocommerce-gateway` folder in the `wp-content/plugins` directory.
|
||||
* Activate "Monero Woocommerce Gateway" in your WordPress admin dashboard.
|
||||
|
@ -61,7 +67,7 @@ Note on security: using this option, while the most secure, requires you to run
|
|||
* `Monero wallet RPC port` (if confirmation type is `monero-wallet-rpc`) - Port the wallet rpc is bound to with the `--rpc-bind-port` argument. (Default 18080)
|
||||
* `Testnet` - Check this to change the blockchain explorer links to the testnet explorer. (Default: unchecked)
|
||||
* `SSL warnings` - Check this to silence SSL warnings. (Default: unchecked)
|
||||
* `Show QR Code` - Show payment QR codes. There is no Monero software that can read QR codes at this time (Default: unchecked)
|
||||
* `Show QR Code` - Show payment QR codes. (Default: unchecked)
|
||||
* `Show Prices in Monero` - Convert all prices on the frontend to Monero. Experimental feature, only use if you do not accept any other payment option. (Default: unchecked)
|
||||
* `Display Decimals` (if show prices in Monero is enabled) - Number of decimals to round prices to on the frontend. The final order amount will not be rounded and will be displayed down to the nanoMonero. (Default: 12)
|
||||
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 4.3 KiB |
Binary file not shown.
Before Width: | Height: | Size: 413 B |
Binary file not shown.
Before Width: | Height: | Size: 2.8 KiB |
Binary file not shown.
After Width: | Height: | Size: 9.4 KiB |
Binary file not shown.
After Width: | Height: | Size: 941 B |
Binary file not shown.
After Width: | Height: | Size: 4.1 KiB |
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2018, Ryo Currency Project
|
||||
* Copyright (c) 2018, Fireice Trust Fund
|
||||
*/
|
||||
function monero_showNotification(message, type='success') {
|
||||
var toast = jQuery('<div class="' + type + '"><span>' + message + '</span></div>');
|
||||
|
@ -62,7 +62,7 @@ function monero_updateDetails() {
|
|||
break;
|
||||
}
|
||||
|
||||
jQuery('#monero_exchange_rate').html('1 XMR = '+details.rate_formatted+' '+details.currency);
|
||||
jQuery('#monero_exchange_rate').html('1 WOW = '+details.rate_formatted+' '+details.currency);
|
||||
jQuery('#monero_total_amount').html(details.amount_total_formatted);
|
||||
jQuery('#monero_total_paid').html(details.amount_paid_formatted);
|
||||
jQuery('#monero_total_due').html(details.amount_due_formatted);
|
||||
|
@ -87,7 +87,7 @@ function monero_updateDetails() {
|
|||
'<a href="'+monero_explorer_url+'/tx/'+tx.txid+'" target="_blank">'+tx.txid+'</a>'+
|
||||
'</td>'+
|
||||
'<td>'+height+'</td>'+
|
||||
'<td>'+tx.amount_formatted+' Monero</td>'+
|
||||
'<td>'+tx.amount_formatted+' Wownero</td>'+
|
||||
'</tr>';
|
||||
|
||||
jQuery('#monero_tx_table tbody').append(row);
|
||||
|
@ -110,7 +110,7 @@ function monero_updateDetails() {
|
|||
}
|
||||
}
|
||||
if(is_new_tx) {
|
||||
monero_showNotification('Transaction received for '+new_txs[i].amount_formatted+' Monero');
|
||||
monero_showNotification('Transaction received for '+new_txs[i].amount_formatted+' Wownero');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright (c) 2018, Ryo Currency Project
|
||||
* Copyright (c) 2018, Fireice Trust Fund
|
||||
* Admin interface for Monero gateway
|
||||
* Authors: mosu-forge
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
@ -27,7 +26,7 @@ class Monero_Admin_Interface {
|
|||
public function meta_boxes() {
|
||||
add_meta_box(
|
||||
'monero_admin_order_details',
|
||||
__('Monero Gateway','monero_gateway'),
|
||||
__('Wownero Gateway','wownero_gateway'),
|
||||
array($this, 'meta_box_order_details'),
|
||||
'shop_order',
|
||||
'normal',
|
||||
|
@ -47,28 +46,28 @@ class Monero_Admin_Interface {
|
|||
*/
|
||||
public function admin_menu() {
|
||||
add_menu_page(
|
||||
__('Monero', 'monero_gateway'),
|
||||
__('Monero', 'monero_gateway'),
|
||||
__('Wownero', 'wownero_gateway'),
|
||||
__('Wownero', 'wownero_gateway'),
|
||||
'manage_woocommerce',
|
||||
'monero_gateway',
|
||||
'wownero_gateway',
|
||||
array($this, 'orders_page'),
|
||||
MONERO_GATEWAY_PLUGIN_URL.'/assets/images/monero-icon-admin.png',
|
||||
MONERO_GATEWAY_PLUGIN_URL.'/assets/images/wownero-icon-admin.png',
|
||||
56 // Position on menu, woocommerce has 55.5, products has 55.6
|
||||
);
|
||||
|
||||
add_submenu_page(
|
||||
'monero_gateway',
|
||||
__('Payments', 'monero_gateway'),
|
||||
__('Payments', 'monero_gateway'),
|
||||
'wownero_gateway',
|
||||
__('Payments', 'wownero_gateway'),
|
||||
__('Payments', 'wownero_gateway'),
|
||||
'manage_woocommerce',
|
||||
'monero_gateway_payments',
|
||||
'wownero_gateway_payments',
|
||||
array($this, 'payments_page')
|
||||
);
|
||||
|
||||
$settings_page = add_submenu_page(
|
||||
'monero_gateway',
|
||||
__('Settings', 'monero_gateway'),
|
||||
__('Settings', 'monero_gateway'),
|
||||
'wownero_gateway',
|
||||
__('Settings', 'wownero_gateway'),
|
||||
__('Settings', 'wownero_gateway'),
|
||||
'manage_options',
|
||||
'monero_gateway_settings',
|
||||
array($this, 'settings_page')
|
||||
|
@ -81,8 +80,8 @@ class Monero_Admin_Interface {
|
|||
*/
|
||||
public function admin_menu_update() {
|
||||
global $submenu;
|
||||
if (isset($submenu['monero_gateway'])) {
|
||||
unset($submenu['monero_gateway'][0]);
|
||||
if (isset($submenu['wownero_gateway'])) {
|
||||
unset($submenu['wownero_gateway'][0]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -105,7 +104,7 @@ class Monero_Admin_Interface {
|
|||
public function settings_page_init() {
|
||||
global $current_tab, $current_section;
|
||||
|
||||
$current_section = 'monero_gateway';
|
||||
$current_section = 'wownero_gateway';
|
||||
$current_tab = 'checkout';
|
||||
|
||||
// Include settings pages.
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright (c) 2018, Ryo Currency Project
|
||||
* Copyright (c) 2018, Fireice Trust Fund
|
||||
* Admin interface for Monero gateway
|
||||
* Authors: mosu-forge
|
||||
*/
|
||||
|
||||
if(!class_exists('WP_List_Table')) {
|
||||
|
@ -54,7 +53,7 @@ class Monero_Admin_Payments_List extends WP_List_Table {
|
|||
} else {
|
||||
$tab_info['all']['active'] = 'class="current" aria-current="page"';
|
||||
}
|
||||
if(Monero_Gateway::get_confirm_type() == 'monero-wallet-rpc') {
|
||||
if(Monero_Gateway::get_confirm_type() == 'wownero-wallet-rpc') {
|
||||
$balance = Monero_Gateway::admin_balance_info();
|
||||
$balance_info = <<<HTML
|
||||
<div style="border:1px solid #ddd;padding:5px 10px;">
|
||||
|
@ -69,32 +68,32 @@ HTML;
|
|||
}
|
||||
echo <<<HTML
|
||||
<div class="wrap">
|
||||
<h1 class="wp-heading-inline">Monero Payments</h1>
|
||||
<h1 class="wp-heading-inline">Wownero Payments</h1>
|
||||
$balance_info
|
||||
<hr class="wp-header-end">
|
||||
<ul class="subsubsub">
|
||||
<li>
|
||||
<a href="?page=monero_gateway_payments&type=all" {$tab_info['all']['active']}>
|
||||
<a href="?page=wownero_gateway_payments&type=all" {$tab_info['all']['active']}>
|
||||
All <span class="count">({$tab_info['all']['count']})</span>
|
||||
</a> |
|
||||
</li>
|
||||
<li style="display:none">
|
||||
<a href="?page=monero_gateway_payments&type=pending" {$tab_info['pending']['active']}>
|
||||
<a href="?page=wownero_gateway_payments&type=pending" {$tab_info['pending']['active']}>
|
||||
Pending <span class="count">({$tab_info['pending']['count']})</span>
|
||||
</a> |
|
||||
</li>
|
||||
<li>
|
||||
<a href="?page=monero_gateway_payments&type=paid" {$tab_info['paid']['active']}>
|
||||
<a href="?page=wownero_gateway_payments&type=paid" {$tab_info['paid']['active']}>
|
||||
Received <span class="count">({$tab_info['paid']['count']})</span>
|
||||
</a> |
|
||||
</li>
|
||||
<li>
|
||||
<a href="?page=monero_gateway_payments&type=confirmed" {$tab_info['confirmed']['active']}>
|
||||
<a href="?page=wownero_gateway_payments&type=confirmed" {$tab_info['confirmed']['active']}>
|
||||
Confirmed <span class="count">({$tab_info['confirmed']['count']})</span>
|
||||
</a> |
|
||||
</li>
|
||||
<li>
|
||||
<a href="?page=monero_gateway_payments&type=expired" {$tab_info['expired']['active']}>
|
||||
<a href="?page=wownero_gateway_payments&type=expired" {$tab_info['expired']['active']}>
|
||||
Expired <span class="count">({$tab_info['expired']['count']})</span>
|
||||
</a>
|
||||
</li>
|
||||
|
@ -107,7 +106,7 @@ HTML;
|
|||
</p>
|
||||
$hidden_fields
|
||||
</form>
|
||||
<h2 class="screen-reader-text">Monero Payments List</h2>
|
||||
<h2 class="screen-reader-text">Wownero Payments List</h2>
|
||||
<style>
|
||||
#col_order_id { width: 150px; }
|
||||
#col_payment_id { width: 150px; }
|
||||
|
@ -145,7 +144,7 @@ HTML;
|
|||
echo $item->height;
|
||||
break;
|
||||
case 'col_amount':
|
||||
echo Monero_Gateway::format_monero($item->amount).' Monero';
|
||||
echo Monero_Gateway::format_monero($item->amount).' Wownero';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -200,7 +199,7 @@ HTML;
|
|||
}
|
||||
|
||||
public function no_items() {
|
||||
esc_html_e('No Monero payments found', 'monero_gateway');
|
||||
esc_html_e('No Wownero payments found', 'wownero_gateway');
|
||||
}
|
||||
|
||||
protected function get_filter_vars() {
|
||||
|
|
|
@ -4,111 +4,117 @@ defined( 'ABSPATH' ) || exit;
|
|||
|
||||
return array(
|
||||
'enabled' => array(
|
||||
'title' => __('Enable / Disable', 'monero_gateway'),
|
||||
'label' => __('Enable this payment gateway', 'monero_gateway'),
|
||||
'title' => __('Enable / Disable', 'wownero_gateway'),
|
||||
'label' => __('Enable this payment gateway', 'wownero_gateway'),
|
||||
'type' => 'checkbox',
|
||||
'default' => 'no'
|
||||
),
|
||||
'title' => array(
|
||||
'title' => __('Title', 'monero_gateway'),
|
||||
'title' => __('Title', 'wownero_gateway'),
|
||||
'type' => 'text',
|
||||
'desc_tip' => __('Payment title the customer will see during the checkout process.', 'monero_gateway'),
|
||||
'default' => __('Monero Gateway', 'monero_gateway')
|
||||
'desc_tip' => __('Payment title the customer will see during the checkout process.', 'wownero_gateway'),
|
||||
'default' => __('Wownero Gateway', 'wownero_gateway')
|
||||
),
|
||||
'description' => array(
|
||||
'title' => __('Description', 'monero_gateway'),
|
||||
'title' => __('Description', 'wownero_gateway'),
|
||||
'type' => 'textarea',
|
||||
'desc_tip' => __('Payment description the customer will see during the checkout process.', 'monero_gateway'),
|
||||
'default' => __('Pay securely using Monero. You will be provided payment details after checkout.', 'monero_gateway')
|
||||
'desc_tip' => __('Payment description the customer will see during the checkout process.', 'wownero_gateway'),
|
||||
'default' => __('Pay securely using Wownero. You will be provided payment details after checkout.', 'wownero_gateway')
|
||||
),
|
||||
'discount' => array(
|
||||
'title' => __('Discount for using Monero', 'monero_gateway'),
|
||||
'desc_tip' => __('Provide a discount to your customers for making a private payment with Monero', 'monero_gateway'),
|
||||
'description' => __('Enter a percentage discount (i.e. 5 for 5%) or leave this empty if you do not wish to provide a discount', 'monero_gateway'),
|
||||
'title' => __('Discount for using Wownero', 'wownero_gateway'),
|
||||
'desc_tip' => __('Provide a discount to your customers for making a private payment with Wownero', 'wownero_gateway'),
|
||||
'description' => __('Enter a percentage discount (i.e. 5 for 5%) or leave this empty if you do not wish to provide a discount', 'wownero_gateway'),
|
||||
'type' => __('number'),
|
||||
'default' => '0'
|
||||
),
|
||||
'valid_time' => array(
|
||||
'title' => __('Order valid time', 'monero_gateway'),
|
||||
'desc_tip' => __('Amount of time order is valid before expiring', 'monero_gateway'),
|
||||
'description' => __('Enter the number of seconds that the funds must be received in after order is placed. 3600 seconds = 1 hour', 'monero_gateway'),
|
||||
'title' => __('Order valid time', 'wownero_gateway'),
|
||||
'desc_tip' => __('Amount of time order is valid before expiring', 'wownero_gateway'),
|
||||
'description' => __('Enter the number of seconds that the funds must be received in after order is placed. 3600 seconds = 1 hour', 'wownero_gateway'),
|
||||
'type' => __('number'),
|
||||
'default' => '3600'
|
||||
),
|
||||
'confirms' => array(
|
||||
'title' => __('Number of confirmations', 'monero_gateway'),
|
||||
'desc_tip' => __('Number of confirms a transaction must have to be valid', 'monero_gateway'),
|
||||
'description' => __('Enter the number of confirms that transactions must have. Enter 0 to zero-confim. Each confirm will take approximately four minutes', 'monero_gateway'),
|
||||
'title' => __('Number of confirmations', 'wownero_gateway'),
|
||||
'desc_tip' => __('Number of confirms a transaction must have to be valid', 'wownero_gateway'),
|
||||
'description' => __('Enter the number of confirms that transactions must have. Enter 0 to zero-confim. Each confirm will take approximately four minutes', 'wownero_gateway'),
|
||||
'type' => __('number'),
|
||||
'default' => '5'
|
||||
'default' => '4'
|
||||
),
|
||||
'confirm_type' => array(
|
||||
'title' => __('Confirmation Type', 'monero_gateway'),
|
||||
'desc_tip' => __('Select the method for confirming transactions', 'monero_gateway'),
|
||||
'description' => __('Select the method for confirming transactions', 'monero_gateway'),
|
||||
'title' => __('Confirmation Type', 'wownero_gateway'),
|
||||
'desc_tip' => __('Select the method for confirming transactions', 'wownero_gateway'),
|
||||
'description' => __('Select the method for confirming transactions', 'wownero_gateway'),
|
||||
'type' => 'select',
|
||||
'options' => array(
|
||||
'viewkey' => __('viewkey', 'monero_gateway'),
|
||||
'monero-wallet-rpc' => __('monero-wallet-rpc', 'monero_gateway')
|
||||
'viewkey' => __('viewkey', 'wownero_gateway'),
|
||||
'wownero-wallet-rpc' => __('wownero-wallet-rpc', 'wownero_gateway')
|
||||
),
|
||||
'default' => 'viewkey'
|
||||
),
|
||||
'monero_address' => array(
|
||||
'title' => __('Monero Address', 'monero_gateway'),
|
||||
'title' => __('Wownero Address', 'wownero_gateway'),
|
||||
'label' => __('Useful for people that have not a daemon online'),
|
||||
'type' => 'text',
|
||||
'desc_tip' => __('Monero Wallet Address (MoneroL)', 'monero_gateway')
|
||||
'desc_tip' => __('Wownero Wallet Address (MoneroL)', 'wownero_gateway')
|
||||
),
|
||||
'viewkey' => array(
|
||||
'title' => __('Secret Viewkey', 'monero_gateway'),
|
||||
'title' => __('Secret Viewkey', 'wownero_gateway'),
|
||||
'label' => __('Secret Viewkey'),
|
||||
'type' => 'text',
|
||||
'desc_tip' => __('Your secret Viewkey', 'monero_gateway')
|
||||
'desc_tip' => __('Your secret Viewkey', 'wownero_gateway')
|
||||
),
|
||||
'daemon_host' => array(
|
||||
'title' => __('Monero wallet RPC Host/IP', 'monero_gateway'),
|
||||
'title' => __('Wownero wallet RPC Host/IP', 'wownero_gateway'),
|
||||
'type' => 'text',
|
||||
'desc_tip' => __('This is the Daemon Host/IP to authorize the payment with', 'monero_gateway'),
|
||||
'desc_tip' => __('This is the Daemon Host/IP to authorize the payment with', 'wownero_gateway'),
|
||||
'default' => '127.0.0.1',
|
||||
),
|
||||
'daemon_port' => array(
|
||||
'title' => __('Monero wallet RPC port', 'monero_gateway'),
|
||||
'title' => __('Wownero wallet RPC port', 'wownero_gateway'),
|
||||
'type' => __('number'),
|
||||
'desc_tip' => __('This is the Wallet RPC port to authorize the payment with', 'monero_gateway'),
|
||||
'default' => '18080',
|
||||
'desc_tip' => __('This is the Wallet RPC port to authorize the payment with', 'wownero_gateway'),
|
||||
'default' => '34567',
|
||||
),
|
||||
'testnet' => array(
|
||||
'title' => __(' Testnet', 'monero_gateway'),
|
||||
'label' => __(' Check this if you are using testnet ', 'monero_gateway'),
|
||||
'title' => __(' Testnet', 'wownero_gateway'),
|
||||
'label' => __(' Check this if you are using testnet ', 'wownero_gateway'),
|
||||
'type' => 'checkbox',
|
||||
'description' => __('Advanced usage only', 'monero_gateway'),
|
||||
'description' => __('Advanced usage only', 'wownero_gateway'),
|
||||
'default' => 'no'
|
||||
),
|
||||
'onion_service' => array(
|
||||
'title' => __(' SSL warnings ', 'monero_gateway'),
|
||||
'label' => __(' Check to Silence SSL warnings', 'monero_gateway'),
|
||||
'javascript' => array(
|
||||
'title' => __(' Javascript', 'wownero_gateway'),
|
||||
'label' => __(' Check this to ENABLE Javascript in Checkout page ', 'wownero_gateway'),
|
||||
'type' => 'checkbox',
|
||||
'description' => __('Check this box if you are running on an Onion Service (Suppress SSL errors)', 'monero_gateway'),
|
||||
'default' => 'no'
|
||||
),
|
||||
'onion_service' => array(
|
||||
'title' => __(' SSL warnings ', 'wownero_gateway'),
|
||||
'label' => __(' Check to Silence SSL warnings', 'wownero_gateway'),
|
||||
'type' => 'checkbox',
|
||||
'description' => __('Check this box if you are running on an Onion Service (Suppress SSL errors)', 'wownero_gateway'),
|
||||
'default' => 'no'
|
||||
),
|
||||
'show_qr' => array(
|
||||
'title' => __('Show QR Code', 'monero_gateway'),
|
||||
'label' => __('Show QR Code', 'monero_gateway'),
|
||||
'title' => __('Show QR Code', 'wownero_gateway'),
|
||||
'label' => __('Show QR Code', 'wownero_gateway'),
|
||||
'type' => 'checkbox',
|
||||
'description' => __('Enable this to show a QR code after checkout with payment details.'),
|
||||
'default' => 'no'
|
||||
),
|
||||
'use_monero_price' => array(
|
||||
'title' => __('Show Prices in Monero', 'monero_gateway'),
|
||||
'label' => __('Show Prices in Monero', 'monero_gateway'),
|
||||
'title' => __('Show Prices in Wownero', 'wownero_gateway'),
|
||||
'label' => __('Show Prices in Wownero', 'wownero_gateway'),
|
||||
'type' => 'checkbox',
|
||||
'description' => __('Enable this to convert ALL prices on the frontend to Monero (experimental)'),
|
||||
'description' => __('Enable this to convert ALL prices on the frontend to Wownero (experimental)'),
|
||||
'default' => 'no'
|
||||
),
|
||||
'use_monero_price_decimals' => array(
|
||||
'title' => __('Display Decimals', 'monero_gateway'),
|
||||
'title' => __('Display Decimals', 'wownero_gateway'),
|
||||
'type' => __('number'),
|
||||
'description' => __('Number of decimal places to display on frontend. Upon checkout exact price will be displayed.'),
|
||||
'default' => 12,
|
||||
'default' => 11,
|
||||
),
|
||||
);
|
||||
|
|
|
@ -1,354 +1,356 @@
|
|||
<?php
|
||||
/*
|
||||
* base58.php
|
||||
/**
|
||||
*
|
||||
* PHP Base58 codec
|
||||
* monerophp/base58
|
||||
*
|
||||
* Based on https://github.com/MoneroPy/moneropy/base58.py and https://github.com/mymonero/mymonero-core-js/cryptonote_utils/cryptonote_base58.js
|
||||
* A PHP Base58 codec
|
||||
* https://github.com/monero-integrations/monerophp
|
||||
*
|
||||
* Using work from
|
||||
* bigreddmachine [MoneroPy] (https://github.com/bigreddmachine)
|
||||
* Paul Shapiro [mymonero-core-js] (https://github.com/paulshapiro)
|
||||
*
|
||||
* @author Monero Integrations Team <support@monerointegrations.com> (https://github.com/monero-integrations)
|
||||
* @copyright 2018
|
||||
* @license MIT
|
||||
*
|
||||
* ============================================================================
|
||||
*
|
||||
* // Initialize class
|
||||
* $base58 = new base58();
|
||||
*
|
||||
* // Encode a hexadecimal (base16) string as base58
|
||||
* $encoded = $base58->encode('0137F8F06C971B168745F562AA107B4D172F336271BC0F9D3B510C14D3460DFB27D8CEBE561E73AC1E11833D5EA40200EB3C82E9C66ACAF1AB1A6BB53C40537C0B7A22160B0E');
|
||||
*
|
||||
* // Decode
|
||||
* $decoded = $base58->decode('479cG5opa54beQWSyqNoWw5tna9sHUNmMTtiFqLPaUhDevpJ2YLwXAggSx5ePdeFrYF8cdbmVRSmp1Kn3t4Y9kFu7rZ7pFw');
|
||||
*
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class Monero_base58 {
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
static $alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
|
||||
static $encoded_block_sizes = [0, 2, 3, 5, 6, 7, 9, 10, 11];
|
||||
static $full_block_size = 8;
|
||||
static $full_encoded_block_size = 11;
|
||||
|
||||
/**
|
||||
*
|
||||
* Convert a hexadecimal string to a binary array
|
||||
*
|
||||
* @param string $hex A hexadecimal string to convert to a binary array
|
||||
* @return array
|
||||
*
|
||||
*/
|
||||
private function hex_to_bin($hex) {
|
||||
if (gettype($hex) != 'string') {
|
||||
throw new Exception('base58->hex_to_bin(): Invalid input type (must be a string)');
|
||||
}
|
||||
if (strlen($hex) % 2 != 0) {
|
||||
throw new Exception('base58->hex_to_bin(): Invalid input length (must be even)');
|
||||
}
|
||||
|
||||
$res = array_fill(0, strlen($hex) / 2, 0);
|
||||
for ($i = 0; $i < strlen($hex) / 2; $i++) {
|
||||
$res[$i] = intval(substr($hex, $i * 2, $i * 2 + 2 - $i * 2), 16);
|
||||
}
|
||||
return $res;
|
||||
class Monero_base58
|
||||
{
|
||||
static $alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
|
||||
static $encoded_block_sizes = [0, 2, 3, 5, 6, 7, 9, 10, 11];
|
||||
static $full_block_size = 8;
|
||||
static $full_encoded_block_size = 11;
|
||||
/**
|
||||
*
|
||||
* Convert a hexadecimal string to a binary array
|
||||
*
|
||||
* @param string $hex A hexadecimal string to convert to a binary array
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
*/
|
||||
private function hex_to_bin($hex)
|
||||
{
|
||||
if (gettype($hex) != 'string') {
|
||||
throw new Exception('base58->hex_to_bin(): Invalid input type (must be a string)');
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Convert a binary array to a hexadecimal string
|
||||
*
|
||||
* @param array $bin A binary array to convert to a hexadecimal string
|
||||
* @return string
|
||||
*
|
||||
*/
|
||||
private function bin_to_hex($bin) {
|
||||
if (gettype($bin) != 'array') {
|
||||
throw new Exception('base58->bin_to_hex(): Invalid input type (must be an array)');
|
||||
}
|
||||
|
||||
$res = [];
|
||||
for ($i = 0; $i < count($bin); $i++) {
|
||||
$res[] = substr('0'.dechex($bin[$i]), -2);
|
||||
}
|
||||
return join($res);
|
||||
if (strlen($hex) % 2 != 0) {
|
||||
throw new Exception('base58->hex_to_bin(): Invalid input length (must be even)');
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Convert a string to a binary array
|
||||
*
|
||||
* @param string $str A string to convert to a binary array
|
||||
* @return array
|
||||
*
|
||||
*/
|
||||
private function str_to_bin($str) {
|
||||
if (gettype($str) != 'string') {
|
||||
throw new Exception('base58->str_to_bin(): Invalid input type (must be a string)');
|
||||
}
|
||||
|
||||
$res = array_fill(0, strlen($str), 0);
|
||||
for ($i = 0; $i < strlen($str); $i++) {
|
||||
$res[$i] = ord($str[$i]);
|
||||
}
|
||||
return $res;
|
||||
$res = array_fill(0, strlen($hex) / 2, 0);
|
||||
for ($i = 0; $i < strlen($hex) / 2; $i++) {
|
||||
$res[$i] = intval(substr($hex, $i * 2, $i * 2 + 2 - $i * 2), 16);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Convert a binary array to a string
|
||||
*
|
||||
* @param array $bin A binary array to convert to a string
|
||||
* @return string
|
||||
*
|
||||
*/
|
||||
private function bin_to_str($bin) {
|
||||
if (gettype($bin) != 'array') {
|
||||
throw new Exception('base58->bin_to_str(): Invalid input type (must be an array)');
|
||||
}
|
||||
|
||||
$res = array_fill(0, count($bin), 0);
|
||||
for ($i = 0; $i < count($bin); $i++) {
|
||||
$res[$i] = chr($bin[$i]);
|
||||
}
|
||||
return preg_replace('/[[:^print:]]/', '', join($res)); // preg_replace necessary to strip errant non-ASCII characters eg. ''
|
||||
return $res;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* Convert a binary array to a hexadecimal string
|
||||
*
|
||||
* @param array $bin A binary array to convert to a hexadecimal string
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
*/
|
||||
private function bin_to_hex($bin)
|
||||
{
|
||||
if (gettype($bin) != 'array') {
|
||||
throw new Exception('base58->bin_to_hex(): Invalid input type (must be an array)');
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Convert a UInt8BE (one unsigned big endian byte) array to UInt64
|
||||
*
|
||||
* @param array $data A UInt8BE array to convert to UInt64
|
||||
* @return number
|
||||
*
|
||||
*/
|
||||
private function uint8_be_to_64($data) {
|
||||
if (gettype($data) != 'array') {
|
||||
throw new Exception ('base58->uint8_be_to_64(): Invalid input type (must be an array)');
|
||||
}
|
||||
|
||||
$res = 0;
|
||||
$i = 0;
|
||||
switch (9 - count($data)) {
|
||||
case 1:
|
||||
$res = bcadd(bcmul($res, bcpow(2, 8)), $data[$i++]);
|
||||
case 2:
|
||||
$res = bcadd(bcmul($res, bcpow(2, 8)), $data[$i++]);
|
||||
case 3:
|
||||
$res = bcadd(bcmul($res, bcpow(2, 8)), $data[$i++]);
|
||||
case 4:
|
||||
$res = bcadd(bcmul($res, bcpow(2, 8)), $data[$i++]);
|
||||
case 5:
|
||||
$res = bcadd(bcmul($res, bcpow(2, 8)), $data[$i++]);
|
||||
case 6:
|
||||
$res = bcadd(bcmul($res, bcpow(2, 8)), $data[$i++]);
|
||||
case 7:
|
||||
$res = bcadd(bcmul($res, bcpow(2, 8)), $data[$i++]);
|
||||
case 8:
|
||||
$res = bcadd(bcmul($res, bcpow(2, 8)), $data[$i++]);
|
||||
break;
|
||||
default:
|
||||
throw new Exception('base58->uint8_be_to_64: Invalid input length (1 <= count($data) <= 8)');
|
||||
}
|
||||
return $res;
|
||||
$res = [];
|
||||
for ($i = 0; $i < count($bin); $i++) {
|
||||
$res[] = substr('0'.dechex($bin[$i]), -2);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Convert a UInt64 (unsigned 64 bit integer) to a UInt8BE array
|
||||
*
|
||||
* @param number $num A UInt64 number to convert to a UInt8BE array
|
||||
* @param integer $size Size of array to return
|
||||
* @return array
|
||||
*
|
||||
*/
|
||||
private function uint64_to_8_be($num, $size) {
|
||||
if (gettype($num) != ('integer' || 'double')) {
|
||||
throw new Exception ('base58->uint64_to_8_be(): Invalid input type ($num must be a number)');
|
||||
}
|
||||
if (gettype($size) != 'integer') {
|
||||
throw new Exception ('base58->uint64_to_8_be(): Invalid input type ($size must be an integer)');
|
||||
}
|
||||
if ($size < 1 || $size > 8) {
|
||||
throw new Exception ('base58->uint64_to_8_be(): Invalid size (1 <= $size <= 8)');
|
||||
}
|
||||
|
||||
$res = array_fill(0, $size, 0);
|
||||
for ($i = $size - 1; $i >= 0; $i--) {
|
||||
$res[$i] = bcmod($num, bcpow(2, 8));
|
||||
$num = bcdiv($num, bcpow(2, 8));
|
||||
}
|
||||
return $res;
|
||||
return join($res);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* Convert a string to a binary array
|
||||
*
|
||||
* @param string $str A string to convert to a binary array
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
*/
|
||||
private function str_to_bin($str)
|
||||
{
|
||||
if (gettype($str) != 'string') {
|
||||
throw new Exception('base58->str_to_bin(): Invalid input type (must be a string)');
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Convert a hexadecimal (Base16) array to a Base58 string
|
||||
*
|
||||
* @param array $data
|
||||
* @param array $buf
|
||||
* @param number $index
|
||||
* @return array
|
||||
*
|
||||
*/
|
||||
private function encode_block($data, $buf, $index) {
|
||||
if (gettype($data) != 'array') {
|
||||
throw new Exception('base58->encode_block(): Invalid input type ($data must be an array)');
|
||||
}
|
||||
if (gettype($buf) != 'array') {
|
||||
throw new Exception('base58->encode_block(): Invalid input type ($buf must be an array)');
|
||||
}
|
||||
if (gettype($index) != ('integer' || 'double')) {
|
||||
throw new Exception('base58->encode_block(): Invalid input type ($index must be a number)');
|
||||
}
|
||||
if (count($data) < 1 or count($data) > self::$full_encoded_block_size) {
|
||||
throw new Exception('base58->encode_block(): Invalid input length (1 <= count($data) <= 8)');
|
||||
}
|
||||
|
||||
$num = self::uint8_be_to_64($data);
|
||||
$i = self::$encoded_block_sizes[count($data)] - 1;
|
||||
while ($num > 0) {
|
||||
$remainder = bcmod($num, 58);
|
||||
$num = bcdiv($num, 58);
|
||||
$buf[$index + $i] = ord(self::$alphabet[$remainder]);
|
||||
$i--;
|
||||
}
|
||||
return $buf;
|
||||
$res = array_fill(0, strlen($str), 0);
|
||||
for ($i = 0; $i < strlen($str); $i++) {
|
||||
$res[$i] = ord($str[$i]);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Encode a hexadecimal (Base16) string to Base58
|
||||
*
|
||||
* @param string $hex A hexadecimal (Base16) string to convert to Base58
|
||||
* @return string
|
||||
*
|
||||
*/
|
||||
public function encode($hex) {
|
||||
if (gettype($hex) != 'string') {
|
||||
throw new Exception ('base58->encode(): Invalid input type (must be a string)');
|
||||
}
|
||||
|
||||
$data = self::hex_to_bin($hex);
|
||||
if (count($data) == 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$full_block_count = floor(count($data) / self::$full_block_size);
|
||||
$last_block_size = count($data) % self::$full_block_size;
|
||||
$res_size = $full_block_count * self::$full_encoded_block_size + self::$encoded_block_sizes[$last_block_size];
|
||||
|
||||
$res = array_fill(0, $res_size, 0);
|
||||
for ($i = 0; $i < $res_size; $i++) {
|
||||
$res[$i] = self::$alphabet[0];
|
||||
}
|
||||
|
||||
for ($i = 0; $i < $full_block_count; $i++) {
|
||||
$res = self::encode_block(array_slice($data, $i * self::$full_block_size, ($i * self::$full_block_size + self::$full_block_size) - ($i * self::$full_block_size)), $res, $i * self::$full_encoded_block_size);
|
||||
}
|
||||
|
||||
if ($last_block_size > 0) {
|
||||
$res = self::encode_block(array_slice($data, $full_block_count * self::$full_block_size, $full_block_count * self::$full_block_size + $last_block_size), $res, $full_block_count * self::$full_encoded_block_size);
|
||||
}
|
||||
|
||||
return self::bin_to_str($res);
|
||||
return $res;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* Convert a binary array to a string
|
||||
*
|
||||
* @param array $bin A binary array to convert to a string
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
*/
|
||||
private function bin_to_str($bin)
|
||||
{
|
||||
if (gettype($bin) != 'array') {
|
||||
throw new Exception('base58->bin_to_str(): Invalid input type (must be an array)');
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Convert a Base58 input to hexadecimal (Base16)
|
||||
*
|
||||
* @param array $data
|
||||
* @param array $buf
|
||||
* @param integer $index
|
||||
* @return array
|
||||
*
|
||||
*/
|
||||
private function decode_block($data, $buf, $index) {
|
||||
if (gettype($data) != 'array') {
|
||||
throw new Exception('base58->decode_block(): Invalid input type ($data must be an array)');
|
||||
}
|
||||
if (gettype($buf) != 'array') {
|
||||
throw new Exception('base58->decode_block(): Invalid input type ($buf must be an array)');
|
||||
}
|
||||
if (gettype($index) != ('integer' || 'double')) {
|
||||
throw new Exception('base58->decode_block(): Invalid input type ($index must be a number)');
|
||||
}
|
||||
|
||||
$res_size = self::index_of(self::$encoded_block_sizes, count($data));
|
||||
if ($res_size <= 0) {
|
||||
throw new Exception('base58->decode_block(): Invalid input length ($data must be a value from base58::$encoded_block_sizes)');
|
||||
}
|
||||
|
||||
$res_num = 0;
|
||||
$order = 1;
|
||||
for ($i = count($data) - 1; $i >= 0; $i--) {
|
||||
$digit = strpos(self::$alphabet, chr($data[$i]));
|
||||
if ($digit < 0) {
|
||||
throw new Exception("base58->decode_block(): Invalid character ($digit \"{$digit}\" not found in base58::$alphabet)");
|
||||
}
|
||||
|
||||
$product = bcadd(bcmul($order, $digit), $res_num);
|
||||
if ($product > bcpow(2, 64)) {
|
||||
throw new Exception('base58->decode_block(): Integer overflow ($product exceeds the maximum 64bit integer)');
|
||||
}
|
||||
|
||||
$res_num = $product;
|
||||
$order = bcmul($order, 58);
|
||||
}
|
||||
if ($res_size < self::$full_block_size && bcpow(2, 8 * $res_size) <= 0) {
|
||||
throw new Exception('base58->decode_block(): Integer overflow (bcpow(2, 8 * $res_size) exceeds the maximum 64bit integer)');
|
||||
}
|
||||
|
||||
$tmp_buf = self::uint64_to_8_be($res_num, $res_size);
|
||||
for ($i = 0; $i < count($tmp_buf); $i++) {
|
||||
$buf[$i + $index] = $tmp_buf[$i];
|
||||
}
|
||||
return $buf;
|
||||
$res = array_fill(0, count($bin), 0);
|
||||
for ($i = 0; $i < count($bin); $i++) {
|
||||
$res[$i] = chr($bin[$i]);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Decode a Base58 string to hexadecimal (Base16)
|
||||
*
|
||||
* @param string $hex A Base58 string to convert to hexadecimal (Base16)
|
||||
* @return string
|
||||
*
|
||||
*/
|
||||
public function decode($enc) {
|
||||
if (gettype($enc) != 'string') {
|
||||
throw new Exception ('base58->decode(): Invalid input type (must be a string)');
|
||||
}
|
||||
|
||||
$enc = self::str_to_bin($enc);
|
||||
if (count($enc) == 0) {
|
||||
return '';
|
||||
}
|
||||
$full_block_count = floor(bcdiv(count($enc), self::$full_encoded_block_size));
|
||||
$last_block_size = bcmod(count($enc), self::$full_encoded_block_size);
|
||||
$last_block_decoded_size = self::index_of(self::$encoded_block_sizes, $last_block_size);
|
||||
|
||||
$data_size = $full_block_count * self::$full_block_size + $last_block_decoded_size;
|
||||
|
||||
$data = array_fill(0, $data_size, 0);
|
||||
for ($i = 0; $i < $full_block_count; $i++) {
|
||||
$data = self::decode_block(array_slice($enc, $i * self::$full_encoded_block_size, ($i * self::$full_encoded_block_size + self::$full_encoded_block_size) - ($i * self::$full_encoded_block_size)), $data, $i * self::$full_block_size);
|
||||
}
|
||||
|
||||
if ($last_block_size > 0) {
|
||||
$data = self::decode_block(array_slice($enc, $full_block_count * self::$full_encoded_block_size, $full_block_count * self::$full_encoded_block_size + $last_block_size), $data, $full_block_count * self::$full_block_size);
|
||||
}
|
||||
|
||||
return self::bin_to_hex($data);
|
||||
return preg_replace('/[[:^print:]]/', '', join($res)); // preg_replace necessary to strip errant non-ASCII characters eg. ''
|
||||
}
|
||||
/**
|
||||
*
|
||||
* Convert a UInt8BE (one unsigned big endian byte) array to UInt64
|
||||
*
|
||||
* @param array $data A UInt8BE array to convert to UInt64
|
||||
*
|
||||
* @return number
|
||||
*
|
||||
*/
|
||||
private function uint8_be_to_64($data)
|
||||
{
|
||||
if (gettype($data) != 'array') {
|
||||
throw new Exception ('base58->uint8_be_to_64(): Invalid input type (must be an array)');
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Search an array for a value
|
||||
* Source: https://stackoverflow.com/a/30994678
|
||||
*
|
||||
* @param array $haystack An array to search
|
||||
* @param string $needle A string to search for
|
||||
* @return number The index of the element found (or -1 for no match)
|
||||
*
|
||||
*/
|
||||
private function index_of($haystack, $needle) {
|
||||
if (gettype($haystack) != 'array') {
|
||||
throw new Exception ('base58->decode(): Invalid input type ($haystack must be an array)');
|
||||
}
|
||||
// if (gettype($needle) != 'string') {
|
||||
// throw new Exception ('base58->decode(): Invalid input type ($needle must be a string)');
|
||||
// }
|
||||
|
||||
foreach ($haystack as $key => $value) if ($value === $needle) return $key;
|
||||
return -1;
|
||||
$res = 0;
|
||||
$i = 0;
|
||||
switch (9 - count($data)) {
|
||||
case 1:
|
||||
$res = bcadd(bcmul($res, bcpow(2, 8)), $data[$i++]);
|
||||
case 2:
|
||||
$res = bcadd(bcmul($res, bcpow(2, 8)), $data[$i++]);
|
||||
case 3:
|
||||
$res = bcadd(bcmul($res, bcpow(2, 8)), $data[$i++]);
|
||||
case 4:
|
||||
$res = bcadd(bcmul($res, bcpow(2, 8)), $data[$i++]);
|
||||
case 5:
|
||||
$res = bcadd(bcmul($res, bcpow(2, 8)), $data[$i++]);
|
||||
case 6:
|
||||
$res = bcadd(bcmul($res, bcpow(2, 8)), $data[$i++]);
|
||||
case 7:
|
||||
$res = bcadd(bcmul($res, bcpow(2, 8)), $data[$i++]);
|
||||
case 8:
|
||||
$res = bcadd(bcmul($res, bcpow(2, 8)), $data[$i++]);
|
||||
break;
|
||||
default:
|
||||
throw new Exception('base58->uint8_be_to_64: Invalid input length (1 <= count($data) <= 8)');
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* Convert a UInt64 (unsigned 64 bit integer) to a UInt8BE array
|
||||
*
|
||||
* @param number $num A UInt64 number to convert to a UInt8BE array
|
||||
* @param integer $size Size of array to return
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
*/
|
||||
private function uint64_to_8_be($num, $size)
|
||||
{
|
||||
if (gettype($num) != ('integer' || 'double')) {
|
||||
throw new Exception ('base58->uint64_to_8_be(): Invalid input type ($num must be a number)');
|
||||
}
|
||||
if (gettype($size) != 'integer') {
|
||||
throw new Exception ('base58->uint64_to_8_be(): Invalid input type ($size must be an integer)');
|
||||
}
|
||||
if ($size < 1 || $size > 8) {
|
||||
throw new Exception ('base58->uint64_to_8_be(): Invalid size (1 <= $size <= 8)');
|
||||
}
|
||||
$res = array_fill(0, $size, 0);
|
||||
for ($i = $size - 1; $i >= 0; $i--) {
|
||||
$res[$i] = bcmod($num, bcpow(2, 8));
|
||||
$num = bcdiv($num, bcpow(2, 8));
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* Convert a hexadecimal (Base16) array to a Base58 string
|
||||
*
|
||||
* @param array $data
|
||||
* @param array $buf
|
||||
* @param number $index
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
*/
|
||||
private function encode_block($data, $buf, $index)
|
||||
{
|
||||
if (gettype($data) != 'array') {
|
||||
throw new Exception('base58->encode_block(): Invalid input type ($data must be an array)');
|
||||
}
|
||||
if (gettype($buf) != 'array') {
|
||||
throw new Exception('base58->encode_block(): Invalid input type ($buf must be an array)');
|
||||
}
|
||||
if (gettype($index) != ('integer' || 'double')) {
|
||||
throw new Exception('base58->encode_block(): Invalid input type ($index must be a number)');
|
||||
}
|
||||
if (count($data) < 1 or count($data) > self::$full_encoded_block_size) {
|
||||
throw new Exception('base58->encode_block(): Invalid input length (1 <= count($data) <= 8)');
|
||||
}
|
||||
$num = self::uint8_be_to_64($data);
|
||||
$i = self::$encoded_block_sizes[count($data)] - 1;
|
||||
while ($num > 0) {
|
||||
$remainder = bcmod($num, 58);
|
||||
$num = bcdiv($num, 58);
|
||||
$buf[$index + $i] = ord(self::$alphabet[$remainder]);
|
||||
$i--;
|
||||
}
|
||||
return $buf;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* Encode a hexadecimal (Base16) string to Base58
|
||||
*
|
||||
* @param string $hex A hexadecimal (Base16) string to convert to Base58
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
*/
|
||||
public function encode($hex)
|
||||
{
|
||||
if (gettype($hex) != 'string') {
|
||||
throw new Exception ('base58->encode(): Invalid input type (must be a string)');
|
||||
}
|
||||
$data = self::hex_to_bin($hex);
|
||||
if (count($data) == 0) {
|
||||
return '';
|
||||
}
|
||||
$full_block_count = floor(count($data) / self::$full_block_size);
|
||||
$last_block_size = count($data) % self::$full_block_size;
|
||||
$res_size = $full_block_count * self::$full_encoded_block_size + self::$encoded_block_sizes[$last_block_size];
|
||||
$res = array_fill(0, $res_size, ord(self::$alphabet[0]));
|
||||
for ($i = 0; $i < $full_block_count; $i++) {
|
||||
$res = self::encode_block(array_slice($data, $i * self::$full_block_size, ($i * self::$full_block_size + self::$full_block_size) - ($i * self::$full_block_size)), $res, $i * self::$full_encoded_block_size);
|
||||
}
|
||||
if ($last_block_size > 0) {
|
||||
$res = self::encode_block(array_slice($data, $full_block_count * self::$full_block_size, $full_block_count * self::$full_block_size + $last_block_size), $res, $full_block_count * self::$full_encoded_block_size);
|
||||
}
|
||||
return self::bin_to_str($res);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* Convert a Base58 input to hexadecimal (Base16)
|
||||
*
|
||||
* @param array $data
|
||||
* @param array $buf
|
||||
* @param integer $index
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
*/
|
||||
private function decode_block($data, $buf, $index)
|
||||
{
|
||||
if (gettype($data) != 'array') {
|
||||
throw new Exception('base58->decode_block(): Invalid input type ($data must be an array)');
|
||||
}
|
||||
if (gettype($buf) != 'array') {
|
||||
throw new Exception('base58->decode_block(): Invalid input type ($buf must be an array)');
|
||||
}
|
||||
if (gettype($index) != ('integer' || 'double')) {
|
||||
throw new Exception('base58->decode_block(): Invalid input type ($index must be a number)');
|
||||
}
|
||||
$res_size = self::index_of(self::$encoded_block_sizes, count($data));
|
||||
if ($res_size <= 0) {
|
||||
throw new Exception('base58->decode_block(): Invalid input length ($data must be a value from base58::$encoded_block_sizes)');
|
||||
}
|
||||
$res_num = 0;
|
||||
$order = 1;
|
||||
for ($i = count($data) - 1; $i >= 0; $i--) {
|
||||
$digit = strpos(self::$alphabet, chr($data[$i]));
|
||||
if ($digit < 0) {
|
||||
throw new Exception("base58->decode_block(): Invalid character ($digit \"{$digit}\" not found in base58::$alphabet)");
|
||||
}
|
||||
$product = bcadd(bcmul($order, $digit), $res_num);
|
||||
if ($product > bcpow(2, 64)) {
|
||||
throw new Exception('base58->decode_block(): Integer overflow ($product exceeds the maximum 64bit integer)');
|
||||
}
|
||||
$res_num = $product;
|
||||
$order = bcmul($order, 58);
|
||||
}
|
||||
if ($res_size < self::$full_block_size && bcpow(2, 8 * $res_size) <= 0) {
|
||||
throw new Exception('base58->decode_block(): Integer overflow (bcpow(2, 8 * $res_size) exceeds the maximum 64bit integer)');
|
||||
}
|
||||
|
||||
$tmp_buf = self::uint64_to_8_be($res_num, $res_size);
|
||||
for ($i = 0; $i < count($tmp_buf); $i++) {
|
||||
$buf[$i + $index] = $tmp_buf[$i];
|
||||
}
|
||||
return $buf;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* Decode a Base58 string to hexadecimal (Base16)
|
||||
*
|
||||
* @param string $hex A Base58 string to convert to hexadecimal (Base16)
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
*/
|
||||
public function decode($enc)
|
||||
{
|
||||
if (gettype($enc) != 'string') {
|
||||
throw new Exception ('base58->decode(): Invalid input type (must be a string)');
|
||||
}
|
||||
$enc = self::str_to_bin($enc);
|
||||
if (count($enc) == 0) {
|
||||
return '';
|
||||
}
|
||||
$full_block_count = floor(bcdiv(count($enc), self::$full_encoded_block_size));
|
||||
$last_block_size = bcmod(count($enc), self::$full_encoded_block_size);
|
||||
$last_block_decoded_size = self::index_of(self::$encoded_block_sizes, $last_block_size);
|
||||
$data_size = $full_block_count * self::$full_block_size + $last_block_decoded_size;
|
||||
if ($data_size == -1) {
|
||||
return '';
|
||||
}
|
||||
$data = array_fill(0, $data_size, 0);
|
||||
for ($i = 0; $i <= $full_block_count; $i++) {
|
||||
$data = self::decode_block(array_slice($enc, $i * self::$full_encoded_block_size, ($i * self::$full_encoded_block_size + self::$full_encoded_block_size) - ($i * self::$full_encoded_block_size)), $data, $i * self::$full_block_size);
|
||||
}
|
||||
if ($last_block_size > 0) {
|
||||
$data = self::decode_block(array_slice($enc, $full_block_count * self::$full_encoded_block_size, $full_block_count * self::$full_encoded_block_size + $last_block_size), $data, $full_block_count * self::$full_block_size);
|
||||
}
|
||||
return self::bin_to_hex($data);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* Search an array for a value
|
||||
* Source: https://stackoverflow.com/a/30994678
|
||||
*
|
||||
* @param array $haystack An array to search
|
||||
* @param string $needle A string to search for
|
||||
*)
|
||||
* @return number The index of the element found (or -1 for no match)
|
||||
*
|
||||
*/
|
||||
private function index_of($haystack, $needle)
|
||||
{
|
||||
if (gettype($haystack) != 'array') {
|
||||
throw new Exception ('base58->decode(): Invalid input type ($haystack must be an array)');
|
||||
}
|
||||
// if (gettype($needle) != 'string') {
|
||||
// throw new Exception ('base58->decode(): Invalid input type ($needle must be a string)');
|
||||
// }
|
||||
foreach ($haystack as $key => $value) if ($value === $needle) return $key;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
*
|
||||
* @author Serhack
|
||||
* @author cryptochangements
|
||||
* @author mosu-forge
|
||||
*
|
||||
*/
|
||||
|
||||
|
@ -19,6 +18,7 @@ class Monero_Explorer_Tools
|
|||
public function __construct($testnet = false)
|
||||
{
|
||||
$this->url = $testnet ? MONERO_GATEWAY_TESTNET_EXPLORER_URL : MONERO_GATEWAY_MAINNET_EXPLORER_URL;
|
||||
$this->url = preg_replace("/\/+$/", "", $this->url);
|
||||
}
|
||||
|
||||
private function call_api($endpoint)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
/*
|
||||
* Main Gateway of Monero using either a local daemon or the explorer
|
||||
* Authors: Serhack, cryptochangements, mosu-forge
|
||||
* Authors: SerHack, cryptochangements
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
@ -10,10 +10,10 @@ require_once('class-monero-cryptonote.php');
|
|||
|
||||
class Monero_Gateway extends WC_Payment_Gateway
|
||||
{
|
||||
private static $_id = 'monero_gateway';
|
||||
private static $_title = 'Monero Gateway';
|
||||
private static $_method_title = 'Monero Gateway';
|
||||
private static $_method_description = 'Monero Gateway Plug-in for WooCommerce.';
|
||||
private static $_id = 'wownero_gateway';
|
||||
private static $_title = 'Wownero Gateway';
|
||||
private static $_method_title = 'Wownero Gateway';
|
||||
private static $_method_description = 'Wownero Gateway Plug-in for WooCommerce.';
|
||||
private static $_errors = [];
|
||||
|
||||
private static $discount = false;
|
||||
|
@ -35,21 +35,21 @@ class Monero_Gateway extends WC_Payment_Gateway
|
|||
private static $monero_explorer_tools;
|
||||
private static $log;
|
||||
|
||||
private static $currencies = array('BTC','USD','EUR','CAD','INR','GBP','COP','SGD','JPY');
|
||||
private static $currencies = 'USD';
|
||||
private static $rates = array();
|
||||
|
||||
private static $payment_details = array();
|
||||
|
||||
public function get_icon()
|
||||
{
|
||||
return apply_filters('woocommerce_gateway_icon', '<img src="'.MONERO_GATEWAY_PLUGIN_URL.'assets/images/monero-icon.png"/>');
|
||||
return apply_filters('woocommerce_gateway_icon', '<img src="'.MONERO_GATEWAY_PLUGIN_URL.'assets/images/wownero-icon.png"/>', $this->id);
|
||||
}
|
||||
|
||||
function __construct($add_action=true)
|
||||
{
|
||||
$this->id = self::$_id;
|
||||
$this->method_title = __(self::$_method_title, 'monero_gateway');
|
||||
$this->method_description = __(self::$_method_description, 'monero_gateway');
|
||||
$this->method_title = __(self::$_method_title, 'wownero_gateway');
|
||||
$this->method_description = __(self::$_method_description, 'wownero_gateway');
|
||||
$this->has_fields = false;
|
||||
$this->supports = array(
|
||||
'products',
|
||||
|
@ -87,13 +87,17 @@ class Monero_Gateway extends WC_Payment_Gateway
|
|||
$explorer_url = self::$testnet ? MONERO_GATEWAY_TESTNET_EXPLORER_URL : MONERO_GATEWAY_MAINNET_EXPLORER_URL;
|
||||
defined('MONERO_GATEWAY_EXPLORER_URL') || define('MONERO_GATEWAY_EXPLORER_URL', $explorer_url);
|
||||
|
||||
// Add the currency of the shop to $currencies array. Needed for do_update_event() function
|
||||
$currency_shop = get_woocommerce_currency();
|
||||
array_push(self::$currencies, $currency_shop);
|
||||
|
||||
if($add_action)
|
||||
add_action('woocommerce_update_options_payment_gateways_'.$this->id, array($this, 'process_admin_options'));
|
||||
|
||||
// Initialize helper classes
|
||||
self::$cryptonote = new Monero_Cryptonote();
|
||||
if(self::$confirm_type == 'monero-wallet-rpc') {
|
||||
require_once('class-monero-wallet-rpc.php');
|
||||
if(self::$confirm_type == 'wownero-wallet-rpc') {
|
||||
require_once('class-wownero-wallet-rpc.php');
|
||||
self::$monero_wallet_rpc = new Monero_Wallet_Rpc(self::$host, self::$port);
|
||||
} else {
|
||||
require_once('class-monero-explorer-tools.php');
|
||||
|
@ -111,10 +115,10 @@ class Monero_Gateway extends WC_Payment_Gateway
|
|||
public function validate_monero_address_field($key,$address)
|
||||
{
|
||||
if($this->settings['confirm_type'] == 'viewkey') {
|
||||
if (strlen($address) == 95 && substr($address, 0, 1) == '4')
|
||||
if (strlen($address) == 97)
|
||||
if(self::$cryptonote->verify_checksum($address))
|
||||
return $address;
|
||||
self::$_errors[] = 'Monero address is invalid';
|
||||
self::$_errors[] = 'Wownero address is invalid';
|
||||
}
|
||||
return $address;
|
||||
}
|
||||
|
@ -149,7 +153,7 @@ class Monero_Gateway extends WC_Payment_Gateway
|
|||
public function admin_options()
|
||||
{
|
||||
$confirm_type = self::$confirm_type;
|
||||
if($confirm_type === 'monero-wallet-rpc')
|
||||
if($confirm_type === 'wownero-wallet-rpc')
|
||||
$balance = self::admin_balance_info();
|
||||
|
||||
$settings_html = $this->generate_settings_html(array(), false);
|
||||
|
@ -169,8 +173,8 @@ class Monero_Gateway extends WC_Payment_Gateway
|
|||
$wallet_amount = self::$monero_wallet_rpc->getbalance();
|
||||
$height = self::$monero_wallet_rpc->getheight();
|
||||
if (!isset($wallet_amount)) {
|
||||
self::$_errors[] = 'Cannot connect to monero-wallet-rpc';
|
||||
self::$log->add('Monero_Payments', '[ERROR] Cannot connect to monero-wallet-rpc');
|
||||
self::$_errors[] = 'Cannot connect to wownero-wallet-rpc';
|
||||
self::$log->add('Monero_Payments', '[ERROR] Cannot connect to wownero-wallet-rpc');
|
||||
return array(
|
||||
'height' => 'Not Available',
|
||||
'balance' => 'Not Available',
|
||||
|
@ -179,8 +183,8 @@ class Monero_Gateway extends WC_Payment_Gateway
|
|||
} else {
|
||||
return array(
|
||||
'height' => $height,
|
||||
'balance' => self::format_monero($wallet_amount['balance']).' Monero',
|
||||
'unlocked_balance' => self::format_monero($wallet_amount['unlocked_balance']).' Monero'
|
||||
'balance' => self::format_monero($wallet_amount['balance']).' Wownero',
|
||||
'unlocked_balance' => self::format_monero($wallet_amount['unlocked_balance']).' Wownero'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -209,18 +213,40 @@ class Monero_Gateway extends WC_Payment_Gateway
|
|||
|
||||
$order = wc_get_order($order_id);
|
||||
|
||||
// Generate a unique payment id
|
||||
do {
|
||||
$payment_id = bin2hex(openssl_random_pseudo_bytes(8));
|
||||
$query = $wpdb->prepare("SELECT COUNT(*) FROM $table_name WHERE payment_id=%s", array($payment_id));
|
||||
$payment_id_used = $wpdb->get_var($query);
|
||||
} while ($payment_id_used);
|
||||
if(self::$confirm_type != 'wownero-wallet-rpc') {
|
||||
// Generate a unique payment id
|
||||
do {
|
||||
$payment_id = bin2hex(openssl_random_pseudo_bytes(8));
|
||||
$query = $wpdb->prepare("SELECT COUNT(*) FROM $table_name WHERE payment_id=%s", array($payment_id));
|
||||
$payment_id_used = $wpdb->get_var($query);
|
||||
} while ($payment_id_used);
|
||||
}
|
||||
else {
|
||||
// Generate subaddress
|
||||
$payment_id = self::$monero_wallet_rpc->create_address(0, 'Order: ' . $order_id);
|
||||
if(isset($payment_id['address'])) {
|
||||
$payment_id = $payment_id['address'];
|
||||
}
|
||||
else {
|
||||
self::$log->add('Monero_Gateway', 'Couldn\'t create subaddress for order ' . $order_id);
|
||||
}
|
||||
}
|
||||
|
||||
$currency = $order->get_currency();
|
||||
$rate = self::get_live_rate($currency);
|
||||
$fiat_amount = $order->get_total('');
|
||||
$monero_amount = 1e8 * $fiat_amount / $rate;
|
||||
|
||||
|
||||
if($rate != 0)
|
||||
$monero_amount = 1e8 * $fiat_amount / $rate;
|
||||
else{
|
||||
// Critical, the price has not been retrivied.
|
||||
$monero_amount = -1;
|
||||
$error_message = "The price for Wownero could not be retrieved. Please contact the merchant.";
|
||||
self::$log->add('Monero_Payments', "[ERROR] Impossible to retrieve price for order: ".$order_id);
|
||||
wc_add_notice( __('Payment error:', 'woothemes') . $error_message, 'error' );
|
||||
return;
|
||||
}
|
||||
|
||||
if(self::$discount)
|
||||
$monero_amount = $monero_amount - $monero_amount * self::$discount / 100;
|
||||
|
||||
|
@ -229,8 +255,8 @@ class Monero_Gateway extends WC_Payment_Gateway
|
|||
$query = $wpdb->prepare("INSERT INTO $table_name (order_id, payment_id, currency, rate, amount) VALUES (%d, %s, %s, %d, %d)", array($order_id, $payment_id, $currency, $rate, $monero_amount));
|
||||
$wpdb->query($query);
|
||||
|
||||
$order->update_status('on-hold', __('Awaiting offline payment', 'monero_gateway'));
|
||||
$order->reduce_order_stock(); // Reduce stock levels
|
||||
$order->update_status('on-hold', __('Awaiting offline payment', 'wownero_gateway'));
|
||||
wc_reduce_stock_levels( $order_id );
|
||||
WC()->cart->empty_cart(); // Remove cart
|
||||
|
||||
return array(
|
||||
|
@ -249,7 +275,7 @@ class Monero_Gateway extends WC_Payment_Gateway
|
|||
|
||||
// Get Live Price
|
||||
$currencies = implode(',', self::$currencies);
|
||||
$api_link = 'https://min-api.cryptocompare.com/data/price?fsym=XMR&tsyms='.$currencies.'&extraParams=monero_woocommerce';
|
||||
$api_link = 'https://api.coingecko.com/api/v3/coins/wownero/tickers?page=1';
|
||||
$curl = curl_init();
|
||||
curl_setopt_array($curl, array(
|
||||
CURLOPT_RETURNTRANSFER => 1,
|
||||
|
@ -257,20 +283,28 @@ class Monero_Gateway extends WC_Payment_Gateway
|
|||
));
|
||||
$resp = curl_exec($curl);
|
||||
curl_close($curl);
|
||||
$price = json_decode($resp, true);
|
||||
$json = json_decode($resp, true);
|
||||
$price = $json['tickers'][0]['converted_last']['usd'];
|
||||
|
||||
if(!isset($price['Response']) || $price['Response'] != 'Error') {
|
||||
$table_name = $wpdb->prefix.'monero_gateway_live_rates';
|
||||
foreach($price as $currency=>$rate) {
|
||||
// shift decimal eight places for precise int storage
|
||||
$rate = intval($rate * 1e8);
|
||||
$query = $wpdb->prepare("INSERT INTO $table_name (currency, rate, updated) VALUES (%s, %d, NOW()) ON DUPLICATE KEY UPDATE rate=%d, updated=NOW()", array($currency, $rate, $rate));
|
||||
$wpdb->query($query);
|
||||
$query = $wpdb->prepare("INSERT INTO `$table_name` (currency, rate, updated) VALUES (%s, %d, NOW()) ON DUPLICATE KEY UPDATE rate=%d, updated=NOW()", array( $currency, $rate, $rate));
|
||||
$result = $wpdb->query($query);
|
||||
if(!$result){
|
||||
self::$log->add('Wownero_Payments', "[ERROR] Impossible to write DB. Please check your DB connection or enable Debugging.");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else{
|
||||
self::$log->add('Monero_Payments', "[ERROR] Unable to fetch prices from coingecko.com.");
|
||||
}
|
||||
|
||||
// Get current network/wallet height
|
||||
if(self::$confirm_type == 'monero-wallet-rpc')
|
||||
if(self::$confirm_type == 'wownero-wallet-rpc')
|
||||
$height = self::$monero_wallet_rpc->getheight();
|
||||
else
|
||||
$height = self::$monero_explorer_tools->getheight();
|
||||
|
@ -306,7 +340,7 @@ class Monero_Gateway extends WC_Payment_Gateway
|
|||
$payment_id = self::sanatize_id($quote->payment_id);
|
||||
$amount_monero = $quote->amount_total;
|
||||
|
||||
if(self::$confirm_type == 'monero-wallet-rpc')
|
||||
if(self::$confirm_type == 'wownero-wallet-rpc')
|
||||
$new_txs = self::check_payment_rpc($payment_id);
|
||||
else
|
||||
$new_txs = self::check_payment_explorer($payment_id);
|
||||
|
@ -360,9 +394,9 @@ class Monero_Gateway extends WC_Payment_Gateway
|
|||
unset(self::$payment_details[$order_id]);
|
||||
|
||||
if(self::is_virtual_in_cart($order_id) == true){
|
||||
$order->update_status('completed', __('Payment has been received.', 'monero_gateway'));
|
||||
$order->update_status('completed', __('Payment has been received.', 'wownero_gateway'));
|
||||
} else {
|
||||
$order->update_status('processing', __('Payment has been received.', 'monero_gateway'));
|
||||
$order->update_status('processing', __('Payment has been received.', 'wownero_gateway'));
|
||||
}
|
||||
|
||||
} else if($paid) {
|
||||
|
@ -383,22 +417,41 @@ class Monero_Gateway extends WC_Payment_Gateway
|
|||
|
||||
unset(self::$payment_details[$order_id]);
|
||||
|
||||
$order->update_status('cancelled', __('Payment has expired.', 'monero_gateway'));
|
||||
$order->update_status('cancelled', __('Payment has expired.', 'wownero_gateway'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static function check_payment_rpc($payment_id)
|
||||
protected static function check_payment_rpc($subaddress)
|
||||
{
|
||||
$txs = array();
|
||||
$payments = self::$monero_wallet_rpc->get_all_payments($payment_id);
|
||||
foreach($payments as $payment) {
|
||||
$txs[] = array(
|
||||
'amount' => $payment['amount'],
|
||||
'txid' => $payment['tx_hash'],
|
||||
'height' => $payment['block_height']
|
||||
);
|
||||
$address_index = self::$monero_wallet_rpc->get_address_index($subaddress);
|
||||
if(isset($address_index['index']['minor'])){
|
||||
$address_index = $address_index['index']['minor'];
|
||||
}
|
||||
else {
|
||||
self::$log->add('Monero_Gateway', '[ERROR] Couldn\'t get address index of subaddress: ' . $subaddress);
|
||||
return $txs;
|
||||
}
|
||||
$payments = self::$monero_wallet_rpc->get_transfers(array( 'in' => true, 'pool' => true, 'subaddr_indices' => array($address_index)));
|
||||
if(isset($payments['in'])) {
|
||||
foreach($payments['in'] as $payment) {
|
||||
$txs[] = array(
|
||||
'amount' => $payment['amount'],
|
||||
'txid' => $payment['txid'],
|
||||
'height' => $payment['height']
|
||||
);
|
||||
}
|
||||
}
|
||||
if(isset($payments['pool'])) {
|
||||
foreach($payments['pool'] as $payment) {
|
||||
$txs[] = array(
|
||||
'amount' => $payment['amount'],
|
||||
'txid' => $payment['txid'],
|
||||
'height' => $payment['height']
|
||||
);
|
||||
}
|
||||
}
|
||||
return $txs;
|
||||
}
|
||||
|
@ -476,14 +529,8 @@ class Monero_Gateway extends WC_Payment_Gateway
|
|||
$address = self::$address;
|
||||
$payment_id = self::sanatize_id($details[0]->payment_id);
|
||||
|
||||
if(self::$confirm_type == 'monero-wallet-rpc') {
|
||||
$array_integrated_address = self::$monero_wallet_rpc->make_integrated_address($payment_id);
|
||||
if (isset($array_integrated_address['integrated_address'])) {
|
||||
$integrated_addr = $array_integrated_address['integrated_address'];
|
||||
} else {
|
||||
self::$log->add('Monero_Gateway', '[ERROR] Unable get integrated address');
|
||||
return '[ERROR] Unable get integrated address';
|
||||
}
|
||||
if(self::$confirm_type == 'wownero-wallet-rpc') {
|
||||
$integrated_addr = $payment_id;
|
||||
} else {
|
||||
if ($address) {
|
||||
$decoded_address = self::$cryptonote->decode_address($address);
|
||||
|
@ -491,8 +538,8 @@ class Monero_Gateway extends WC_Payment_Gateway
|
|||
$pub_viewkey = $decoded_address['viewkey'];
|
||||
$integrated_addr = self::$cryptonote->integrated_addr_from_keys($pub_spendkey, $pub_viewkey, $payment_id);
|
||||
} else {
|
||||
self::$log->add('Monero_Gateway', '[ERROR] Merchant has not set Monero address');
|
||||
return '[ERROR] Merchant has not set Monero address';
|
||||
self::$log->add('Wownero_Gateway', '[ERROR] Merchant has not set Wownero address');
|
||||
return '[ERROR] Merchant has not set Wownero address';
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -519,7 +566,8 @@ class Monero_Gateway extends WC_Payment_Gateway
|
|||
}
|
||||
}
|
||||
|
||||
$qrcode_uri = 'monero:'.$address.'?tx_amount='.$amount_due.'&tx_payment_id='.$payment_id;
|
||||
$amount_formatted = self::format_monero($amount_due);
|
||||
$qrcode_uri = 'monero:'.$integrated_addr.'?tx_amount='.$amount_formatted.'&tx_payment_id='.$payment_id;
|
||||
$my_order_url = wc_get_endpoint_url('view-order', $order_id, wc_get_page_permalink('myaccount'));
|
||||
|
||||
$payment_details = array(
|
||||
|
@ -558,26 +606,29 @@ class Monero_Gateway extends WC_Payment_Gateway
|
|||
$user = wp_get_current_user();
|
||||
if($user === 0)
|
||||
self::ajax_output(array('error' => '[ERROR] User not logged in'));
|
||||
|
||||
if(isset($_GET['order_id'])){
|
||||
$order_id = preg_replace("/[^0-9]+/", "", $_GET['order_id']);
|
||||
$order = wc_get_order($order_id);
|
||||
|
||||
if($order->get_customer_id() != $user->ID)
|
||||
self::ajax_output(array('error' => '[ERROR] Order does not belong to this user'));
|
||||
|
||||
$order_id = preg_replace("/[^0-9]+/", "", $_GET['order_id']);
|
||||
$order = wc_get_order( $order_id );
|
||||
|
||||
if($order->user_id != $user->ID)
|
||||
self::ajax_output(array('error' => '[ERROR] Order does not belong to this user'));
|
||||
|
||||
if($order->get_payment_method() != self::$_id)
|
||||
self::ajax_output(array('error' => '[ERROR] Order not paid for with Monero'));
|
||||
|
||||
$details = self::get_payment_details($order);
|
||||
if(!is_array($details))
|
||||
self::ajax_output(array('error' => $details));
|
||||
|
||||
self::ajax_output($details);
|
||||
|
||||
if($order->get_payment_method() != self::$_id)
|
||||
self::ajax_output(array('error' => '[ERROR] Order not paid for with Wownero'));
|
||||
|
||||
$details = self::get_payment_details($order);
|
||||
if(!is_array($details))
|
||||
self::ajax_output(array('error' => $details));
|
||||
|
||||
self::ajax_output($details);
|
||||
}
|
||||
}
|
||||
public static function ajax_output($response) {
|
||||
ob_clean();
|
||||
header('Content-type: application/json');
|
||||
if (ob_get_length() > 0){
|
||||
ob_clean();
|
||||
}
|
||||
echo json_encode($response);
|
||||
wp_die();
|
||||
}
|
||||
|
@ -675,7 +726,7 @@ class Monero_Gateway extends WC_Payment_Gateway
|
|||
<span class="woocommerce-Price-amount amount" data-price="$price" data-currency="$currency"
|
||||
data-rate="$rate" data-rate-type="live">
|
||||
$monero_amount_formatted
|
||||
<span class="woocommerce-Price-currencySymbol">XMR</span>
|
||||
<span class="woocommerce-Price-currencySymbol">WOW</span>
|
||||
</span>
|
||||
|
||||
HTML;
|
||||
|
@ -706,7 +757,7 @@ HTML;
|
|||
<span class="woocommerce-Price-amount amount" data-price="$price" data-currency="$currency"
|
||||
data-rate="$rate" data-rate-type="fixed">
|
||||
$monero_amount_formatted
|
||||
<span class="woocommerce-Price-currencySymbol">XMR</span>
|
||||
<span class="woocommerce-Price-currencySymbol">WOW</span>
|
||||
</span>
|
||||
|
||||
HTML;
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
* @author Kacper Rowinski <krowinski@implix.com>
|
||||
* http://implix.com
|
||||
* Modified to work with monero-rpc wallet by Serhack and cryptochangements
|
||||
* Modified to work with monero-wallet-rpc wallet by mosu-forge
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
@ -178,7 +177,7 @@ class Monero_Wallet_Rpc
|
|||
// check for curl error
|
||||
if (0 < curl_errno($ch)) {
|
||||
if(is_admin())
|
||||
echo '[ERROR] Failed to connect to monero-wallet-rpc at ' . $this->host . ' port '. $this->port .'</br>';
|
||||
echo '[ERROR] Failed to connect to wownero-wallet-rpc at ' . $this->host . ' port '. $this->port .'</br>';
|
||||
}
|
||||
// close the connection
|
||||
curl_close($ch);
|
||||
|
@ -244,13 +243,6 @@ class Monero_Wallet_Rpc
|
|||
return $incoming_transfers;
|
||||
}
|
||||
|
||||
public function get_transfers($input_type, $input_value)
|
||||
{
|
||||
$get_parameters = array($input_type => $input_value);
|
||||
$get_transfers = $this->_run('get_transfers', $get_parameters);
|
||||
return $get_transfers;
|
||||
}
|
||||
|
||||
public function view_key()
|
||||
{
|
||||
$query_key = array('key_type' => 'view_key');
|
||||
|
@ -349,4 +341,30 @@ class Monero_Wallet_Rpc
|
|||
$get_bulk_payments = $this->_run('get_bulk_payments', $get_bulk_payments_parameters);
|
||||
return $get_bulk_payments;
|
||||
}
|
||||
|
||||
public function get_transfers($arr)
|
||||
{
|
||||
$get_parameters = $arr;
|
||||
$get_transfers = $this->_run('get_transfers', $get_parameters);
|
||||
return $get_transfers;
|
||||
}
|
||||
|
||||
public function get_address_index($subaddress)
|
||||
{
|
||||
$params = array('address' => $subaddress);
|
||||
return $this->_run('get_address_index', $params);
|
||||
}
|
||||
|
||||
public function store()
|
||||
{
|
||||
return $this->_run('store');
|
||||
}
|
||||
|
||||
public function create_address($account_index = 0, $label = '')
|
||||
{
|
||||
$params = array('account_index' => $account_index, 'label' => $label);
|
||||
$create_address_method = $this->_run('create_address', $params);
|
||||
$save = $this->store(); // Save wallet state after subaddress creation
|
||||
return $create_address_method;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,24 +2,24 @@
|
|||
/*
|
||||
Plugin Name: Monero Woocommerce Gateway
|
||||
Plugin URI: https://github.com/monero-integrations/monerowp
|
||||
Description: Extends WooCommerce by adding a Monero Gateway
|
||||
Version: 3.0.0
|
||||
Description: Extends WooCommerce by adding a Wownero Gateway
|
||||
Version: 3.0.1
|
||||
Tested up to: 4.9.8
|
||||
Author: mosu-forge, SerHack
|
||||
Author: SerHack
|
||||
Author URI: https://monerointegrations.com/
|
||||
*/
|
||||
// This code isn't for Dark Net Markets, please report them to Authority!
|
||||
// This code is for Dark Net Markets
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
// Constants, you can edit these if you fork this repo
|
||||
define('MONERO_GATEWAY_MAINNET_EXPLORER_URL', 'https://xmrchain.net/');
|
||||
define('MONERO_GATEWAY_MAINNET_EXPLORER_URL', 'https://explore.wownero.com/');
|
||||
define('MONERO_GATEWAY_TESTNET_EXPLORER_URL', 'https://testnet.xmrchain.com/');
|
||||
define('MONERO_GATEWAY_ADDRESS_PREFIX', 0x12);
|
||||
define('MONERO_GATEWAY_ADDRESS_PREFIX_INTEGRATED', 0x13);
|
||||
define('MONERO_GATEWAY_ATOMIC_UNITS', 12);
|
||||
define('MONERO_GATEWAY_ADDRESS_PREFIX', 0x1032);
|
||||
define('MONERO_GATEWAY_ADDRESS_PREFIX_INTEGRATED', 0x1A9A);
|
||||
define('MONERO_GATEWAY_ATOMIC_UNITS', 11);
|
||||
define('MONERO_GATEWAY_ATOMIC_UNIT_THRESHOLD', 10); // Amount under in atomic units payment is valid
|
||||
define('MONERO_GATEWAY_DIFFICULTY_TARGET', 120);
|
||||
define('MONERO_GATEWAY_DIFFICULTY_TARGET', 300);
|
||||
|
||||
// Do not edit these constants
|
||||
define('MONERO_GATEWAY_PLUGIN_DIR', plugin_dir_path(__FILE__));
|
||||
|
@ -43,8 +43,8 @@ function monero_init() {
|
|||
// Include our Admin interface class
|
||||
require_once('include/admin/class-monero-admin-interface.php');
|
||||
|
||||
add_filter('woocommerce_payment_gateways', 'monero_gateway');
|
||||
function monero_gateway($methods) {
|
||||
add_filter('woocommerce_payment_gateways', 'wownero_gateway');
|
||||
function wownero_gateway($methods) {
|
||||
$methods[] = 'Monero_Gateway';
|
||||
return $methods;
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ function monero_init() {
|
|||
add_filter('plugin_action_links_' . plugin_basename(__FILE__), 'monero_payment');
|
||||
function monero_payment($links) {
|
||||
$plugin_links = array(
|
||||
'<a href="'.admin_url('admin.php?page=monero_gateway_settings').'">'.__('Settings', 'monero_gateway').'</a>'
|
||||
'<a href="'.admin_url('admin.php?page=monero_gateway_settings').'">'.__('Settings', 'wownero_gateway').'</a>'
|
||||
);
|
||||
return array_merge($plugin_links, $links);
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ function monero_init() {
|
|||
function monero_cron_add_one_minute($schedules) {
|
||||
$schedules['one_minute'] = array(
|
||||
'interval' => 60,
|
||||
'display' => __('Once every minute', 'monero_gateway')
|
||||
'display' => __('Once every minute', 'wownero_gateway')
|
||||
);
|
||||
return $schedules;
|
||||
}
|
||||
|
@ -100,15 +100,15 @@ function monero_init() {
|
|||
|
||||
add_filter('woocommerce_currencies', 'monero_add_currency');
|
||||
function monero_add_currency($currencies) {
|
||||
$currencies['Monero'] = __('Monero', 'monero_gateway');
|
||||
$currencies['Wownero'] = __('Wownero', 'wownero_gateway');
|
||||
return $currencies;
|
||||
}
|
||||
|
||||
add_filter('woocommerce_currency_symbol', 'monero_add_currency_symbol', 10, 2);
|
||||
function monero_add_currency_symbol($currency_symbol, $currency) {
|
||||
switch ($currency) {
|
||||
case 'Monero':
|
||||
$currency_symbol = 'XMR';
|
||||
case 'Wownero':
|
||||
$currency_symbol = 'WOW';
|
||||
break;
|
||||
}
|
||||
return $currency_symbol;
|
||||
|
@ -180,7 +180,7 @@ function monero_init() {
|
|||
else
|
||||
$rate_formatted = sprintf('%.5f', $rate / 1e8);
|
||||
|
||||
return "<span class=\"monero-price\">1 XMR = $rate_formatted $currency</span>";
|
||||
return "<span class=\"monero-price\">1 WOW = $rate_formatted $currency</span>";
|
||||
}
|
||||
add_shortcode('monero-price', 'monero_price_func');
|
||||
|
||||
|
@ -209,7 +209,7 @@ function monero_install() {
|
|||
if($wpdb->get_var("show tables like '$table_name'") != $table_name) {
|
||||
$sql = "CREATE TABLE $table_name (
|
||||
order_id BIGINT(20) UNSIGNED NOT NULL,
|
||||
payment_id VARCHAR(16) DEFAULT '' NOT NULL,
|
||||
payment_id VARCHAR(95) DEFAULT '' NOT NULL,
|
||||
currency VARCHAR(6) DEFAULT '' NOT NULL,
|
||||
rate BIGINT UNSIGNED DEFAULT 0 NOT NULL,
|
||||
amount BIGINT UNSIGNED DEFAULT 0 NOT NULL,
|
||||
|
@ -226,7 +226,7 @@ function monero_install() {
|
|||
if($wpdb->get_var("show tables like '$table_name'") != $table_name) {
|
||||
$sql = "CREATE TABLE $table_name (
|
||||
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
payment_id VARCHAR(16) DEFAULT '' NOT NULL,
|
||||
payment_id VARCHAR(95) DEFAULT '' NOT NULL,
|
||||
txid VARCHAR(64) DEFAULT '' NOT NULL,
|
||||
amount BIGINT UNSIGNED DEFAULT 0 NOT NULL,
|
||||
height MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,
|
||||
|
|
52
readme.txt
52
readme.txt
|
@ -1,18 +1,19 @@
|
|||
=== Monero WooCommerce Extension ===
|
||||
Contributors: serhack, mosu-forge
|
||||
Contributors: SerHack
|
||||
Donate link: http://monerointegrations.com/donate.html
|
||||
Tags: monero, woocommerce, integration, payment, merchant, cryptocurrency, accept monero, monero woocommerce
|
||||
Requires at least: 4.0
|
||||
Tested up to: 4.9.8
|
||||
Tested up to: 5.0.1
|
||||
Stable tag: trunk
|
||||
License: GPLv2 or later
|
||||
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
License: MIT license
|
||||
License URI: https://github.com/monero-integrations/monerowp/blob/master/LICENSE
|
||||
|
||||
Monero WooCommerce Extension is a Wordpress plugin that allows to accept bitcoins at WooCommerce-powered online stores.
|
||||
Monero WooCommerce Extension is a Wordpress plugin that allows to accept monero at WooCommerce-powered online stores.
|
||||
|
||||
== Description ==
|
||||
|
||||
An extension to WooCommerce for accepting Monero as payment in your store.
|
||||
Your online store must use WooCommerce platform (free wordpress plugin).
|
||||
Once you installed and activated WooCommerce, you may install and activate Monero WooCommerce Extension.
|
||||
|
||||
= Benefits =
|
||||
|
||||
|
@ -21,17 +22,15 @@ An extension to WooCommerce for accepting Monero as payment in your store.
|
|||
* Add monero payments option to your existing online store with alternative main currency.
|
||||
* Flexible exchange rate calculations fully managed via administrative settings.
|
||||
* Zero fees and no commissions for monero payments processing from any third party.
|
||||
* Automatic conversion to Monero via real time exchange rate feed and calculations.
|
||||
* Automatic conversion to Monero via realtime exchange rate feed and calculations.
|
||||
* Ability to set exchange rate calculation multiplier to compensate for any possible losses due to bank conversions and funds transfer fees.
|
||||
|
||||
== Installation ==
|
||||
|
||||
1. Install "Monero WooCommerce extension" WordPress plugin just like any other WordPress plugin.
|
||||
1. Install "Monero WooCommerce extension" wordpress plugin just like any other Wordpress plugin.
|
||||
2. Activate
|
||||
3. Setup your monero-wallet-rpc with a view-only wallet
|
||||
4. Add your monero-wallet-rpc host address and Monero address in the settings panel
|
||||
5. Click “Enable this payment gateway”
|
||||
6. Enjoy it!
|
||||
3. Configure it with your wallet rpc address, (username or password not requested), your monero address
|
||||
4. Enjoy it!
|
||||
|
||||
== Remove plugin ==
|
||||
|
||||
|
@ -47,11 +46,32 @@ An extension to WooCommerce for accepting Monero as payment in your store.
|
|||
= 0.1 =
|
||||
* First version ! Yay!
|
||||
|
||||
= 0.2 =
|
||||
* Bug fixes
|
||||
= 1.0 =
|
||||
* Added the view key option
|
||||
|
||||
= 2.1 =
|
||||
* Verify transactions without monero-wallet-rpc
|
||||
* Optionally accept zero confirmation transactions
|
||||
* bug fixing
|
||||
|
||||
= 2.2 =
|
||||
* Fix some bugs
|
||||
|
||||
= 2.3 =
|
||||
* Bug fixing
|
||||
|
||||
= 3.0.0 =
|
||||
Fuck you mosu-forge
|
||||
* Ability to set number of confirms: 0 for zero conf, up to 60.
|
||||
* Amount owed in XMR gets locked in after the order for a configurable amount of time after which the order is invalid, default 60 minutes.
|
||||
* Shows transactions received along with the number of confirms right on the order success page, auto-updates through AJAX.
|
||||
* QR code generation is done with Javascript instead of sending payment details to a 3rd party.
|
||||
* Admin page for showing all transactions made to the wallet.
|
||||
* Logic is done via cron, instead of the user having to stay on the order page until payment is confirmed.
|
||||
* Payment details (along with the txid) are always visible on the customer's account dashboard on the my orders section.
|
||||
* Live prices are also run via cron, shortcodes for showing exchange rates.
|
||||
* Properly hooks into order confirmation email page.
|
||||
|
||||
= 0.3 =
|
||||
* Complete rewrite of how the plugin handles payments
|
||||
|
||||
== Upgrade Notice ==
|
||||
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
<table class="striped" style="width:100%" cellspacing="0" cellpadding="5">
|
||||
<tr>
|
||||
<td>Exchange rate</td>
|
||||
<td>1 XMR = <?php echo $details['rate_formatted'].' '.$details['currency']; ?></td>
|
||||
<td>1 WOW = <?php echo $details['rate_formatted'].' '.$details['currency']; ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Total amount</td>
|
||||
<td><?php echo $details['amount_total_formatted']; ?> XMR</td>
|
||||
<td><?php echo $details['amount_total_formatted']; ?> WOW</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Total paid</td>
|
||||
<td><?php echo $details['amount_paid_formatted']; ?> XMR</td>
|
||||
<td><?php echo $details['amount_paid_formatted']; ?> WOW</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Total due</td>
|
||||
<td><?php echo $details['amount_due_formatted']; ?> XMR</td>
|
||||
<td><?php echo $details['amount_due_formatted']; ?> WOW</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Order age</td>
|
||||
|
@ -72,10 +72,10 @@
|
|||
<?php foreach($details['txs'] as $tx): ?>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="<?php echo MONERO_GATEWAY_EXPLORER_URL.'/tx/'.$tx['txid']; ?>" target="_blank"><?php echo $tx['txid']; ?></a>
|
||||
<a href="<?php echo MONERO_GATEWAY_EXPLORER_URL.'tx/'.$tx['txid']; ?>" target="_blank"><?php echo $tx['txid']; ?></a>
|
||||
</td>
|
||||
<td><?php echo $tx['height']; ?></td>
|
||||
<td><?php echo sprintf(MONERO_GATEWAY_ATOMIC_UNITS_SPRINTF, $tx['amount'] / MONERO_GATEWAY_ATOMIC_UNITS_POW); ?> XMR</td>
|
||||
<td><?php echo sprintf(MONERO_GATEWAY_ATOMIC_UNITS_SPRINTF, $tx['amount'] / MONERO_GATEWAY_ATOMIC_UNITS_POW); ?> WOW</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</table>
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
<?php foreach($errors as $error): ?>
|
||||
<div class="error"><p><strong>Monero Gateway Error</strong>: <?php echo $error; ?></p></div>
|
||||
<div class="error"><p><strong>Wownero Gateway Error</strong>: <?php echo $error; ?></p></div>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<h1>Monero Gateway Settings</h1>
|
||||
<h1>Wownero Gateway Settings</h1>
|
||||
|
||||
<?php if($confirm_type === 'monero-wallet-rpc'): ?>
|
||||
<?php if($confirm_type === 'wownero-wallet-rpc'): ?>
|
||||
<div style="border:1px solid #ddd;padding:5px 10px;">
|
||||
<?php
|
||||
echo 'Wallet height: ' . $balance['height'] . '</br>';
|
||||
|
@ -18,12 +18,12 @@
|
|||
<?php echo $settings_html ?>
|
||||
</table>
|
||||
|
||||
<h4><a href="https://github.com/monero-integrations/monerowp">Learn more about using the Monero payment gateway</a></h4>
|
||||
<h4><a href="https://github.com/monero-integrations/monerowp">Learn more about using the Wownero payment gateway</a></h4>
|
||||
|
||||
<script>
|
||||
function moneroUpdateFields() {
|
||||
var confirmType = jQuery("#woocommerce_monero_gateway_confirm_type").val();
|
||||
if(confirmType == "monero-wallet-rpc") {
|
||||
if(confirmType == "wownero-wallet-rpc") {
|
||||
jQuery("#woocommerce_monero_gateway_monero_address").closest("tr").hide();
|
||||
jQuery("#woocommerce_monero_gateway_viewkey").closest("tr").hide();
|
||||
jQuery("#woocommerce_monero_gateway_daemon_host").closest("tr").show();
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<?php echo $method_title ?>
|
||||
</h2>
|
||||
|
||||
<p style="margin: 0 0 16px;">Your order has been confirmed. Thank you for paying with Monero!</p>
|
||||
<p style="margin: 0 0 16px;">Your order has been confirmed. Thank you for paying with Wownero!</p>
|
||||
|
||||
<?php elseif($details['status'] == 'expired' || $details['status'] == 'expired_partial'): ?>
|
||||
|
||||
|
@ -37,7 +37,7 @@
|
|||
<td class="td" style="text-align: left; vertical-align: middle; font-family: 'Helvetica Neue', Helvetica, Roboto, Arial, sans-serif; word-wrap: break-word; color: #636363; border: 1px solid #e5e5e5; padding: 12px;">
|
||||
TOTAL DUE: <br/>
|
||||
<strong>
|
||||
<?php echo $details['amount_total_formatted']; ?> XMR
|
||||
<?php echo $details['amount_total_formatted']; ?> WOW
|
||||
</strong>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -45,7 +45,7 @@
|
|||
<td class="td" style="text-align: left; vertical-align: middle; font-family: 'Helvetica Neue', Helvetica, Roboto, Arial, sans-serif; word-wrap: break-word; color: #636363; border: 1px solid #e5e5e5; padding: 12px;">
|
||||
EXCHANGE RATE: <br/>
|
||||
<strong>
|
||||
1 XMR = <?php echo $details['rate_formatted'] . ' ' . $details['currency']; ?>
|
||||
1 WOW = <?php echo $details['rate_formatted'] . ' ' . $details['currency']; ?>
|
||||
</strong>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
<span class="monero_payment_paid">We have received your payment in full. Please wait while amount is confirmed. Approximate confirm time is <span class="monero_confirm_time"></span>. <?php if(is_wc_endpoint_url('order-received')): ?><br/>You can <a href="<?php echo $details['my_order_url']; ?>">check your payment status</a> anytime in your account dashboard.<?php endif; ?></span>
|
||||
|
||||
<span class="monero_payment_confirmed">Your order has been confirmed. Thank you for paying with Monero!</span>
|
||||
<span class="monero_payment_confirmed">Your order has been confirmed. Thank you for paying with Wownero!</span>
|
||||
|
||||
<span class="monero_payment_expired">Your order has expired. Please place another order to complete your purchase.</span>
|
||||
|
||||
|
@ -40,7 +40,7 @@
|
|||
Total due:
|
||||
<strong class="monero_details_row">
|
||||
<span class="monero_details_main">
|
||||
<span id="monero_total_due"></span> XMR
|
||||
<span id="monero_total_due"></span> WOW
|
||||
</span>
|
||||
<span class="monero_details_right button-row">
|
||||
<button href="#" class="button clipboard" title="Copy Amount"
|
||||
|
@ -53,13 +53,13 @@
|
|||
<li style="display:none">
|
||||
Total order amount:
|
||||
<strong>
|
||||
<span id="monero_total_amount"></span> XMR
|
||||
<span id="monero_total_amount"></span> WOW
|
||||
</strong>
|
||||
</li>
|
||||
<li>
|
||||
Total paid:
|
||||
<strong>
|
||||
<span id="monero_total_paid"></span> XMR
|
||||
<span id="monero_total_paid"></span> WOW
|
||||
</strong>
|
||||
</li>
|
||||
<li>
|
||||
|
|
Loading…
Reference in New Issue