Version 4.7.13
This commit is contained in:
1 parent
fd22d99e69
commit
7124a02564
954 files changed
+29277
-59308
No files matched your search
@@ -306,10 +306,10 @@ class _BillingAgreement extends \IPS\Patterns\ActiveRecord
|
||||
{
|
||||
$transaction = $this->latestUnclaimedTransaction();
|
||||
}
|
||||
/* If there isn't one yet, it might just be that PayPal hasn't taken it yet. Let it try for up to 5 days before giving up, which is how long PayPal will try for if there's an issue. */
|
||||
/* If there isn't one yet, it might just be that PayPal hasn't taken it yet. Let it try for up to 11 days before giving up, which is how long PayPal will try for if there's an issue. */
|
||||
catch ( \OutOfRangeException $e )
|
||||
{
|
||||
if ( $this->next_cycle->getTimestamp() > ( time() - ( 86400 * 5 ) ) )
|
||||
if ( $this->next_cycle->getTimestamp() > ( time() - ( 86400 * 11 ) ) )
|
||||
{
|
||||
throw new \UnderflowException;
|
||||
}
|
||||
|
||||
@@ -323,7 +323,7 @@ class _Customer extends \IPS\Member
|
||||
{
|
||||
try
|
||||
{
|
||||
$location = \IPS\GeoLocation::getByIp( \IPS\Request::i()->ipAddress() );
|
||||
$location = \IPS\GeoLocation::getRequesterLocation();
|
||||
\IPS\Request::i()->setCookie( 'location', $this->_addMemberToAddress( $location ) );
|
||||
}
|
||||
catch ( \Exception $e )
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Domain Name Lookup
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
* @package Invision Community
|
||||
* @subpackage Nexus
|
||||
* @since 11 Oct 2016
|
||||
*/
|
||||
|
||||
namespace IPS\nexus;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Domain Name Lookup
|
||||
*/
|
||||
class _DomainLookup
|
||||
{
|
||||
const DOMAIN_TYPE_DOMAIN = 1;
|
||||
const DOMAIN_TYPE_SUBDOMAIN = 2;
|
||||
|
||||
/**
|
||||
* Domain Name
|
||||
*/
|
||||
public $domain;
|
||||
|
||||
/**
|
||||
* Domain Name Type
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* TLD
|
||||
*/
|
||||
public $tld;
|
||||
|
||||
/**
|
||||
* SLD
|
||||
*/
|
||||
public $sld;
|
||||
|
||||
/**
|
||||
* WHOIS
|
||||
*/
|
||||
public $whois = array();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $domain The domain name
|
||||
* @param bool $performWhoisLookup if TRUE, a WHOIS lookup will be attempted
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function __construct( $domain, $performWhoisLookup=TRUE )
|
||||
{
|
||||
$this->domain = $domain;
|
||||
|
||||
$domainData = $this->_findDomainData( NULL, $domain, json_decode( file_get_contents( \IPS\ROOT_PATH . '/applications/nexus/data/domains.json' ), TRUE ) );
|
||||
if ( $domainData === NULL )
|
||||
{
|
||||
throw new \InvalidArgumentException;
|
||||
}
|
||||
|
||||
$this->tld = $domainData['name'];
|
||||
$this->sld = mb_substr( $domain, 0, - mb_strlen( $this->tld ) - 1 );
|
||||
$this->type = ( mb_substr_count( $this->sld, '.' ) > 0 ) ? static::DOMAIN_TYPE_SUBDOMAIN : static::DOMAIN_TYPE_DOMAIN;
|
||||
|
||||
if ( $performWhoisLookup )
|
||||
{
|
||||
foreach ( $domainData['whoisServers'] as $whoisServer )
|
||||
{
|
||||
if ( $resource = @fsockopen( $whoisServer, 43 ) )
|
||||
{
|
||||
\fwrite( $resource, $domain . "\r\n" );
|
||||
$response = '';
|
||||
while( !feof( $resource ) )
|
||||
{
|
||||
$response .= \fgets( $resource, 8192 );
|
||||
}
|
||||
fclose( $resource );
|
||||
|
||||
$this->whois[] = $response;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the data for the deepest domain level
|
||||
*
|
||||
* @param array|null $currentVal The value to return if no deeper value exists
|
||||
* @param array $domain Domain being examined
|
||||
* @return array|null
|
||||
*/
|
||||
protected function _findDomainData( $currentVal, $domain, $jsonData )
|
||||
{
|
||||
foreach ( $jsonData['domains'] as $data )
|
||||
{
|
||||
if ( mb_substr( $domain, - ( mb_strlen( $data['name'] ) + 1 ) ) === ".{$data['name']}" )
|
||||
{
|
||||
return $this->_findDomainData( $data, $domain, $data );
|
||||
}
|
||||
}
|
||||
|
||||
return $currentVal;
|
||||
}
|
||||
}
|
||||
Whitespace-only changes.
@@ -1,146 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief eNom Domain Registrar
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
* @package Invision Community
|
||||
* @subpackage Nexus
|
||||
* @since 08 Aug 2014
|
||||
*/
|
||||
|
||||
namespace IPS\nexus\DomainRegistrar;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* eNom Domain Registrar
|
||||
*/
|
||||
class _Enom
|
||||
{
|
||||
/**
|
||||
* @brief Username
|
||||
*/
|
||||
protected $username;
|
||||
|
||||
/**
|
||||
* @brief Password
|
||||
*/
|
||||
protected $password;
|
||||
|
||||
/**
|
||||
* @brief URL
|
||||
*/
|
||||
protected $url;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $username eNom Reseller Username
|
||||
* @param string $password eNom Reseller Password
|
||||
* @return void
|
||||
*/
|
||||
public function __construct( $username, $password )
|
||||
{
|
||||
$this->username = $username;
|
||||
$this->password = $password;
|
||||
|
||||
$this->url = \IPS\Http\Url::external( 'http://' . ( \IPS\NEXUS_TEST_GATEWAYS ? 'resellertest.enom.com' : 'reseller.enom.com' ) . '/interface.asp' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check Domain Availablility
|
||||
*
|
||||
* @param string $sld SLD
|
||||
* @param string $tld TLD
|
||||
* @return bool
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function check( $sld, $tld )
|
||||
{
|
||||
$response = $this->url->request()->post( array(
|
||||
'command' => 'check',
|
||||
'uid' => $this->username,
|
||||
'pw' => $this->password,
|
||||
'sld' => $sld,
|
||||
'tld' => $tld,
|
||||
'responsetype' => 'xml',
|
||||
) )->decodeXml();
|
||||
|
||||
if ( $response->errors )
|
||||
{
|
||||
throw new \RuntimeException( (string) $response->errors->Err1 );
|
||||
}
|
||||
|
||||
return (string) $response->RRPCode === '210';
|
||||
}
|
||||
|
||||
/**
|
||||
* Register Domain
|
||||
*
|
||||
* @param string $sld SLD
|
||||
* @param string $tld TLD
|
||||
* @param array $nameservers Nameservers
|
||||
* @param \IPS\nexus\Customer $customer The customer
|
||||
* @param \IPS\GeoLocation|NULL $billingAddress Billing Address
|
||||
* @return bool
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function register( $sld, $tld, array $nameservers, \IPS\nexus\Customer $customer, \IPS\GeoLocation $billingAddress = NULL )
|
||||
{
|
||||
$send = array(
|
||||
'command' => 'purchase',
|
||||
'UID' => $this->username,
|
||||
'PW' => $this->password,
|
||||
'SLD' => $sld,
|
||||
'TLD' => $tld,
|
||||
);
|
||||
$i = 1;
|
||||
foreach ( $nameservers as $s )
|
||||
{
|
||||
$send["NS{$i}"] = $s;
|
||||
$i++;
|
||||
}
|
||||
$send['NumYears'] = 1;
|
||||
$send['IgnoreNSFail'] = 'Yes';
|
||||
$send['UseWireTransfer'] = 'Yes';
|
||||
$send['responsetype'] = 'xml';
|
||||
|
||||
$response = $this->url->request( \IPS\LONG_REQUEST_TIMEOUT )->post( $send )->decodeXml();
|
||||
|
||||
if ( $response->errors )
|
||||
{
|
||||
throw new \RuntimeException( (string) $response->errors->Err1 );
|
||||
}
|
||||
|
||||
return (string) $response->RRPCode === '200';
|
||||
}
|
||||
|
||||
/**
|
||||
* Renew Domain
|
||||
*
|
||||
* @param string $sld SLD
|
||||
* @param string $tld TLD
|
||||
* @param int $years Years to renew for
|
||||
* @return bool
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function renew( $sld, $tld, $years )
|
||||
{
|
||||
$response = $this->url->request( \IPS\LONG_REQUEST_TIMEOUT )->post( array(
|
||||
'command' => 'extend',
|
||||
'UID' => $this->username,
|
||||
'PW' => $this->password,
|
||||
'SLD' => $sld,
|
||||
'TLD' => $tld,
|
||||
'responsetype' => 'xml',
|
||||
) )->decodeXml();
|
||||
|
||||
return (string) $response->RRPCode === '200';
|
||||
}
|
||||
}
|
||||
Whitespace-only changes.
@@ -23,21 +23,33 @@ if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
*/
|
||||
class _Request
|
||||
{
|
||||
/**
|
||||
* @brief Maxmind User ID
|
||||
*/
|
||||
protected ?string $id = NULL;
|
||||
|
||||
/**
|
||||
* @brief Maxmind License Key
|
||||
*/
|
||||
protected ?string $licenseKey = NULL;
|
||||
|
||||
/**
|
||||
* @brief Data that will be posted
|
||||
*/
|
||||
protected $data = array();
|
||||
protected array $data = array();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param bool $session Set session data (set to FALSE if this is being initiated outside the checkout sequence)
|
||||
* @param NULL|string $maxmindKey MaxMind License Key (NULL to get from settings)
|
||||
* @param NULL|string $maxmindId MaxMind User ID (NULL to get from settings)
|
||||
* @return void
|
||||
*/
|
||||
public function __construct( $session=TRUE, $maxmindKey=NULL )
|
||||
public function __construct( $session=TRUE, $maxmindKey=NULL, $maxmindId=NULL )
|
||||
{
|
||||
$this->data['license_key'] = $maxmindKey ?: \IPS\Settings::i()->maxmind_key;
|
||||
$this->licenseKey = $maxmindKey ?: \IPS\Settings::i()->maxmind_key;
|
||||
$this->id = $maxmindId ?: \IPS\Settings::i()->maxmind_id;
|
||||
|
||||
if ( $session )
|
||||
{
|
||||
@@ -65,7 +77,7 @@ class _Request
|
||||
*/
|
||||
public function setIpAddress( $ipAddress )
|
||||
{
|
||||
$this->data['i'] = $ipAddress;
|
||||
$this->data['device']['ip_address'] = $ipAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,8 +99,8 @@ class _Request
|
||||
$this->setShippingAddress( $transaction->invoice->shipaddress );
|
||||
}
|
||||
|
||||
$this->data['order_amount'] = (string) $transaction->amount->amount;
|
||||
$this->data['order_currency'] = $transaction->amount->currency;
|
||||
$this->data['order']['amount'] = (string) $transaction->amount->amount;
|
||||
$this->data['order']['currency'] = $transaction->amount->currency;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -99,10 +111,9 @@ class _Request
|
||||
*/
|
||||
public function setBillingAddress( \IPS\GeoLocation $billingAddress )
|
||||
{
|
||||
$this->data['city'] = $billingAddress->city;
|
||||
$this->data['region'] = $billingAddress->region;
|
||||
$this->data['postal'] = $billingAddress->postalCode;
|
||||
$this->data['country'] = $billingAddress->country;
|
||||
$this->data['billing']['city'] = $billingAddress->city;
|
||||
$this->data['billing']['postal'] = $billingAddress->postalCode;
|
||||
$this->data['billing']['country'] = $billingAddress->country;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -113,11 +124,10 @@ class _Request
|
||||
*/
|
||||
public function setShippingAddress( \IPS\GeoLocation $shippingAddress )
|
||||
{
|
||||
$this->data['shipAddr'] = implode( ', ', $shippingAddress->addressLines );
|
||||
$this->data['shipCity'] = $shippingAddress->city;
|
||||
$this->data['shipRegion'] = $shippingAddress->region;
|
||||
$this->data['shipPostal'] = $shippingAddress->postalCode;
|
||||
$this->data['shipCountry'] = $shippingAddress->country;
|
||||
$this->data['shipping']['address'] = implode( ', ', $shippingAddress->addressLines );
|
||||
$this->data['shipping']['city'] = $shippingAddress->city;
|
||||
$this->data['shipping']['postal'] = $shippingAddress->postalCode;
|
||||
$this->data['shipping']['country'] = $shippingAddress->country;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,7 +139,7 @@ class _Request
|
||||
public function setMember( \IPS\Member $member )
|
||||
{
|
||||
$this->_setEmailProperties( $member->email );
|
||||
$this->data['usernameMD5'] = md5( $member->name );
|
||||
$this->data['account']['username_md5'] = md5( $member->name );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -190,8 +200,8 @@ class _Request
|
||||
$local = \substr( $local, 0, $alias );
|
||||
}
|
||||
|
||||
$this->data['domain'] = $domain;
|
||||
$this->data['emailMD5'] = md5( $local . '@' . $domain );
|
||||
$this->data['email']['domain'] = $domain;
|
||||
$this->data['email']['address'] = md5( $local . '@' . $domain );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -202,7 +212,7 @@ class _Request
|
||||
*/
|
||||
public function setPhone( $phoneNumber )
|
||||
{
|
||||
$this->data['custPhone'] = $phoneNumber;
|
||||
$this->data['billing']['phone_number'] = $phoneNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -213,10 +223,9 @@ class _Request
|
||||
*/
|
||||
public function setCard( $card )
|
||||
{
|
||||
$this->data['txn_type'] = 'creditcard';
|
||||
|
||||
$cardNumber = ( $card instanceof \IPS\nexus\CreditCard ) ? $card->number : $card;
|
||||
$this->data['bin'] = mb_substr( $cardNumber, 0, 6 );
|
||||
$this->data['credit_card']['issuer_id_number'] = mb_substr( $cardNumber, 0, 6 );
|
||||
$this->data['credit_card']['last_digits'] = mb_substr( $cardNumber, -4 );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -227,7 +236,7 @@ class _Request
|
||||
*/
|
||||
public function setTransactionType( $type )
|
||||
{
|
||||
$this->data['txn_type'] = $type;
|
||||
$this->data['event']['txn_type'] = $type;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -238,7 +247,7 @@ class _Request
|
||||
*/
|
||||
public function setAVS( $code )
|
||||
{
|
||||
$this->data['avs_result'] = $code;
|
||||
$this->data['credit_card']['avs_result'] = $code;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -249,7 +258,7 @@ class _Request
|
||||
*/
|
||||
public function setCVV( $result )
|
||||
{
|
||||
$this->data['cvv_result'] = $result ? 'Y' : 'N';
|
||||
$this->data['credit_card']['cvv_result'] = $result ? 'Y' : 'N';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -260,13 +269,13 @@ class _Request
|
||||
*/
|
||||
public function request()
|
||||
{
|
||||
$response = \IPS\Http\Url::external( 'https://minfraud.maxmind.com/app/ccv2r' )->request()->post( $this->data );
|
||||
|
||||
if ( isset( $response->httpHeaders['Content-Type'] ) and preg_match( '/; charset=(.+?)$/', $response->httpHeaders['Content-Type'], $matches ) )
|
||||
$response = \IPS\Http\Url::external( 'https://minfraud.maxmind.com/minfraud/v2.0/insights' )->request()->login( $this->id, $this->licenseKey )->post( json_encode( $this->data ) );
|
||||
|
||||
if ( isset( $response->httpHeaders['Content-Type'] ) and preg_match( '/; charset=(.+?);$/', $response->httpHeaders['Content-Type'], $matches ) )
|
||||
{
|
||||
$response = mb_convert_encoding( $response, 'UTF-8', $matches[1] );
|
||||
}
|
||||
|
||||
|
||||
return new \IPS\nexus\Fraud\MaxMind\Response( (string) $response );
|
||||
}
|
||||
}
|
||||
@@ -38,11 +38,7 @@ class _Response
|
||||
{
|
||||
if ( $data )
|
||||
{
|
||||
foreach ( explode( ';', $data ) as $row )
|
||||
{
|
||||
$exploded = explode( '=', $row );
|
||||
$this->data[ $exploded[0] ] = $exploded[1];
|
||||
}
|
||||
$this->data = json_decode( $data, true );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,7 +94,7 @@ class _Response
|
||||
* @brief minFraud error codes
|
||||
* @see https://dev.maxmind.com/minfraud/#Error_Reporting
|
||||
*/
|
||||
protected static $responseErrors = array( 'INVALID_LICENSE_KEY', 'IP_REQUIRED', 'IP_NOT_FOUND ', 'MAX_REQUESTS_REACHED', 'LICENSE_REQUIRED ');
|
||||
protected static array $responseErrors = array( 'JSON_INVALID', 'REQUEST_INVALID', 'AUTHORIZATION_INVALID', 'ACCOUNT_ID_REQUIRED', 'LICENSE_KEY_REQUIRED', 'INSUFFICIENT_FUNDS', 'PERMISSION_REQUIRED' );
|
||||
|
||||
/**
|
||||
* Does the response include an error?
|
||||
@@ -107,7 +103,7 @@ class _Response
|
||||
*/
|
||||
public function error()
|
||||
{
|
||||
if ( $this->err AND \in_array($this->err, static::$responseErrors ) )
|
||||
if ( $this->error AND \in_array( $this->code, static::$responseErrors ) )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
@@ -121,7 +117,7 @@ class _Response
|
||||
*/
|
||||
public function warning()
|
||||
{
|
||||
if ( $this->err AND !\in_array($this->err, static::$responseErrors ) )
|
||||
if ( $this->error AND !\in_array($this->code, static::$responseErrors ) )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ class _CreditCard extends \IPS\nexus\Customer\CreditCard
|
||||
$cardType = 'amex';
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
/* Check it doesn't already exist */
|
||||
$otherCards = \IPS\Db::i()->select( 'card_data', 'nexus_customer_cards', array( 'card_member=? AND card_method=?', $this->member->member_id, $this->method->id ) );
|
||||
if ( \count( $otherCards ) )
|
||||
@@ -99,7 +99,7 @@ class _CreditCard extends \IPS\nexus\Customer\CreditCard
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Save */
|
||||
$response = $this->method->api( 'vault/credit-card', array(
|
||||
'number' => $card->number,
|
||||
|
||||
@@ -94,7 +94,11 @@ class _Exception extends \DomainException
|
||||
break;
|
||||
|
||||
default:
|
||||
if ( $refund )
|
||||
if( isset( $details ) AND $details[0]['issue'] === 'INSTRUMENT_DECLINED' )
|
||||
{
|
||||
$message = \IPS\Member::loggedIn()->language()->get( 'payment_refused' );
|
||||
}
|
||||
elseif ( $refund )
|
||||
{
|
||||
$message = \IPS\Member::loggedIn()->language()->get( 'refund_failed' );
|
||||
}
|
||||
|
||||
@@ -40,13 +40,13 @@ class _PayPal extends \IPS\nexus\Gateway
|
||||
public function checkValidity( \IPS\nexus\Money $amount, \IPS\GeoLocation $billingAddress = NULL, \IPS\nexus\Customer $customer = NULL, $recurrings = array() )
|
||||
{
|
||||
$settings = json_decode( $this->settings, TRUE );
|
||||
|
||||
|
||||
/* Card payments require name and billing address */
|
||||
if ( isset( $settings['type'] ) and $settings['type'] === 'card' and ( !$customer->cm_first_name or !$customer->cm_last_name or !$billingAddress ) )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ( isset( $settings['type'] ) and $settings['type'] === 'card' and ( !$customer->cm_first_name or !$customer->cm_last_name or !$billingAddress ) )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Check transaction limit */
|
||||
switch ( $amount->currency )
|
||||
{
|
||||
@@ -207,6 +207,22 @@ class _PayPal extends \IPS\nexus\Gateway
|
||||
/* Pass to parent */
|
||||
return parent::checkValidity( $amount, $billingAddress, $customer, $recurrings );
|
||||
}
|
||||
|
||||
/**
|
||||
* [Node] Get Node Description
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function get__description()
|
||||
{
|
||||
$settings = json_decode( $this->settings, TRUE );
|
||||
if( isset( $settings['type'] ) and $settings['type'] === 'card' )
|
||||
{
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'gateway_deprecated', FALSE, array( 'sprintf' => 'PayPal Credit Card' ) );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can store cards?
|
||||
@@ -258,6 +274,7 @@ class _PayPal extends \IPS\nexus\Gateway
|
||||
public function paymentScreen( \IPS\nexus\Invoice $invoice, \IPS\nexus\Money $amount, \IPS\nexus\Customer $member = NULL, $recurrings = array(), $type = 'checkout' )
|
||||
{
|
||||
$settings = json_decode( $this->settings, TRUE );
|
||||
|
||||
if ( isset( $settings['type'] ) and $settings['type'] === 'card' )
|
||||
{
|
||||
return array( 'card' => new \IPS\nexus\Form\CreditCard( $this->id . '_card', NULL, TRUE, array(
|
||||
@@ -285,12 +302,13 @@ class _PayPal extends \IPS\nexus\Gateway
|
||||
* @throws \LogicException Message will be displayed to user
|
||||
*/
|
||||
public function auth( \IPS\nexus\Transaction $transaction, $values, \IPS\nexus\Fraud\MaxMind\Request $maxMind = NULL, $recurrings = array(), $source = NULL )
|
||||
{
|
||||
{
|
||||
/* We need a transaction ID */
|
||||
$transaction->save();
|
||||
|
||||
|
||||
/* Do it */
|
||||
$settings = json_decode( $this->settings, TRUE );
|
||||
|
||||
if ( isset( $settings['type'] ) and $settings['type'] === 'card' )
|
||||
{
|
||||
return $this->_cardAuth( \is_array( $values ) ? $values[ $this->id . '_card' ] : $values, $transaction, $maxMind );
|
||||
@@ -306,24 +324,25 @@ class _PayPal extends \IPS\nexus\Gateway
|
||||
return $this->_billingAgreementAuth( $transaction, $maxMind, $recurrance['term'], $recurrance['items'] );
|
||||
}
|
||||
else
|
||||
{
|
||||
{
|
||||
return $this->_paypalAuth( $transaction, $maxMind );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Authorize Card Payment
|
||||
*
|
||||
* @param \IPS\nexus\CreditCard|\IPS\nexus\Customer\CreditCard $card The card to charge
|
||||
* @param \IPS\nexus\Transaction $transaction Transaction
|
||||
* @param \IPS\nexus\Fraud\MaxMind\Request|NULL $maxMind *If* MaxMind is enabled, the request object will be passed here so gateway can additional data before request is made
|
||||
* @param \IPS\nexus\Fraud\MaxMind\Request|NULL $maxMind *If* MaxMind is enabled, the request object will be passed here so gateway can additional data before request is made
|
||||
* @param string|NULL $source 'checkout' if the customer is doing this at a normal checkout, 'renewal' is an automatically generated renewal invoice, 'manual' is admin manually charging. NULL is unknown
|
||||
* @return \IPS\DateTime|NULL Auth is valid until or NULL to indicate auth is good forever
|
||||
* @throws \LogicException Message will be displayed to user
|
||||
*/
|
||||
protected function _cardAuth( $card, \IPS\nexus\Transaction $transaction, \IPS\nexus\Fraud\MaxMind\Request $maxMind = NULL )
|
||||
protected function _cardAuth( $card, \IPS\nexus\Transaction $transaction, \IPS\nexus\Fraud\MaxMind\Request $maxMind = NULL, ?string $source = NULL )
|
||||
{
|
||||
/* Stored Card */
|
||||
/* Stored Card */
|
||||
if ( $card instanceof \IPS\nexus\Customer\CreditCard )
|
||||
{
|
||||
$payer = array(
|
||||
@@ -346,7 +365,7 @@ class _PayPal extends \IPS\nexus\Gateway
|
||||
\IPS\Session::i()->setMember( $transaction->member );
|
||||
\IPS\Member\Device::loadOrCreate( $transaction->member, FALSE )->updateAfterAuthentication( NULL );
|
||||
}
|
||||
|
||||
|
||||
if ( $maxMind )
|
||||
{
|
||||
$maxMind->setCard( $card );
|
||||
@@ -380,23 +399,31 @@ class _PayPal extends \IPS\nexus\Gateway
|
||||
'cvv2' => $card->ccv,
|
||||
'first_name' => $this->_getFirstName( $transaction ),
|
||||
'last_name' => $this->_getLastName( $transaction ),
|
||||
'billing_address' => $this->_getAddress( $transaction->invoice->billaddress, $transaction->member )
|
||||
'billing_address' => $this->_getAddress( $transaction->invoice->billaddress, $transaction->member, 'card' )
|
||||
)
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/* Send the request */
|
||||
$response = $this->api( 'payments/payment', array(
|
||||
'intent' => 'authorize',
|
||||
'payer' => $payer,
|
||||
'transactions' => array( $this->_getTransactions( $transaction ) ),
|
||||
'redirect_urls' => array(
|
||||
'return_url' => \IPS\Settings::i()->base_url . 'applications/nexus/interface/gateways/paypal.php?nexusTransactionId=' . $transaction->id,
|
||||
'cancel_url' => (string) $transaction->invoice->checkoutUrl(),
|
||||
)
|
||||
) );
|
||||
|
||||
try
|
||||
{
|
||||
/* Send the request */
|
||||
$response = $this->api( 'payments/payment', array(
|
||||
'intent' => 'authorize',
|
||||
'payer' => $payer,
|
||||
'transactions' => array( $this->_getTransactions( $transaction ) ),
|
||||
'redirect_urls' => array(
|
||||
'return_url' => \IPS\Settings::i()->base_url . 'applications/nexus/interface/gateways/paypal.php?nexusTransactionId=' . $transaction->id,
|
||||
'cancel_url' => (string) $transaction->invoice->checkoutUrl(),
|
||||
)
|
||||
) );
|
||||
}
|
||||
catch( \IPS\nexus\Gateway\PayPal\Exception $e )
|
||||
{
|
||||
$this->processException( $transaction, $e );
|
||||
throw $e;
|
||||
}
|
||||
|
||||
/* Set transaction data */
|
||||
$transaction->gw_id = $response['transactions'][0]['related_resources'][0]['authorization']['id']; // The transaction ID for the authorization. At capture, it will be updated again to the capture transaction ID
|
||||
@@ -430,15 +457,32 @@ class _PayPal extends \IPS\nexus\Gateway
|
||||
protected function _paypalAuth( \IPS\nexus\Transaction $transaction, \IPS\nexus\Fraud\MaxMind\Request $maxMind = NULL )
|
||||
{
|
||||
/* Send the request */
|
||||
$response = $this->api( 'payments/payment', array(
|
||||
'intent' => 'authorize',
|
||||
'payer' => array( 'payment_method' => 'paypal' ),
|
||||
'transactions' => array( $this->_getTransactions( $transaction ) ),
|
||||
'redirect_urls' => array(
|
||||
'return_url' => \IPS\Settings::i()->base_url . 'applications/nexus/interface/gateways/paypal.php?nexusTransactionId=' . $transaction->id,
|
||||
'cancel_url' => (string) $transaction->invoice->checkoutUrl(),
|
||||
)
|
||||
) );
|
||||
try
|
||||
{
|
||||
$response = $this->api( 'checkout/orders', array(
|
||||
'intent' => 'AUTHORIZE',
|
||||
'purchase_units' => $this->_getPurchaseUnits( $transaction ),
|
||||
'payment_source' => array(
|
||||
'paypal' => array(
|
||||
'experience_context' => array(
|
||||
'return_url' => \IPS\Settings::i()->base_url . 'applications/nexus/interface/gateways/paypal.php?nexusTransactionId=' . $transaction->id,
|
||||
'cancel_url' => (string) $transaction->invoice->checkoutUrl(),
|
||||
),
|
||||
'email_address' => $transaction->member->email,
|
||||
'name' => array(
|
||||
'given_name' => $this->_getFirstName( $transaction ),
|
||||
'surname' => $this->_getLastName( $transaction )
|
||||
),
|
||||
'address' => ( $transaction->invoice->billaddress instanceof \IPS\GeoLocation ) ? $this->_getAddress( $transaction->invoice->billaddress, $transaction->member ) : NULL,
|
||||
)
|
||||
)
|
||||
), 'POST', true, null, md5( (string) $transaction->invoice->checkoutUrl() . ';' . $transaction->id ), 2 );
|
||||
}
|
||||
catch( \IPS\nexus\Gateway\PayPal\Exception $e )
|
||||
{
|
||||
$this->processException( $transaction, $e );
|
||||
throw $e;
|
||||
}
|
||||
|
||||
/* Set transaction data */
|
||||
$transaction->gw_id = $response['id']; // This is a payment ID ("PAY-XXX"). At this time we do not have a real transaction ID
|
||||
@@ -447,11 +491,12 @@ class _PayPal extends \IPS\nexus\Gateway
|
||||
/* Redirect */
|
||||
foreach ( $response['links'] as $link )
|
||||
{
|
||||
if ( $link['rel'] === 'approval_url' )
|
||||
if ( $link['rel'] === 'payer-action' )
|
||||
{
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::external( $link['href'] ) );
|
||||
}
|
||||
}
|
||||
|
||||
throw new \RuntimeException;
|
||||
}
|
||||
|
||||
@@ -471,10 +516,17 @@ class _PayPal extends \IPS\nexus\Gateway
|
||||
|
||||
/* Work out the name */
|
||||
$titles = array();
|
||||
$initialTerm = null;
|
||||
foreach ( $items as $item )
|
||||
{
|
||||
$titles[] = ( $item->name . ( $item->quantity > 1 ? " x{$item->quantity}" : '' ) );
|
||||
|
||||
if( isset( $item->initialInterval ) AND $item->initialInterval instanceof \DateInterval )
|
||||
{
|
||||
$initialTerm = $item->initialInterval;
|
||||
}
|
||||
}
|
||||
|
||||
$title = implode( ', ', $titles );
|
||||
if ( mb_strlen( $title ) > 127 )
|
||||
{
|
||||
@@ -488,49 +540,51 @@ class _PayPal extends \IPS\nexus\Gateway
|
||||
'description' => $description,
|
||||
'type' => 'DIGITAL',
|
||||
) );
|
||||
|
||||
/* Create a plan */
|
||||
|
||||
/* Set up the billing cycles */
|
||||
$billingCycles = array();
|
||||
$sequence = 1;
|
||||
$renewalAmountIncludingTax = $term->cost->amount->multiply( new \IPS\Math\Number( number_format( $term->tax ? ( ( 1 + $term->tax->rate( $transaction->invoice->billaddress ) ) ) : 1, 4, '.', '' ) ) );
|
||||
$billingCycle = array(
|
||||
|
||||
/* If we have an initial term that is different from the renewal, start with that */
|
||||
if( $initialTerm instanceof \DateInterval OR $transaction->amount->amount->compare( $renewalAmountIncludingTax ) !== 0 )
|
||||
{
|
||||
$intervalToUse = $initialTerm instanceof \DateInterval ? $initialTerm : $term->interval;
|
||||
$billingCycles[] = array(
|
||||
'pricing_scheme' => array(
|
||||
'fixed_price' => array(
|
||||
'currency_code' => $transaction->amount->currency,
|
||||
'value' => $transaction->amount->amountAsString()
|
||||
),
|
||||
),
|
||||
'frequency' => static::_getFrequencyFromInterval( $intervalToUse ),
|
||||
'tenure_type' => 'TRIAL',
|
||||
'sequence' => $sequence,
|
||||
'total_cycles' => 1
|
||||
);
|
||||
$sequence++;
|
||||
}
|
||||
|
||||
/* And now the regular renewals */
|
||||
$billingCycles[] = array(
|
||||
'pricing_scheme' => array(
|
||||
'fixed_price' => array(
|
||||
'currency_code' => $term->cost->currency,
|
||||
'value' => ( new \IPS\nexus\Money( $renewalAmountIncludingTax, $term->cost->currency ) )->amountAsString()
|
||||
),
|
||||
),
|
||||
'frequency' => array(),
|
||||
'frequency' => static::_getFrequencyFromInterval( $term->interval ),
|
||||
'tenure_type' => 'REGULAR',
|
||||
'sequence' => 1,
|
||||
'sequence' => $sequence,
|
||||
'total_cycles' => 0
|
||||
);
|
||||
if ( $term->interval->y )
|
||||
{
|
||||
$billingCycle['frequency']['interval_unit'] = 'YEAR';
|
||||
$billingCycle['frequency']['interval_count'] = $term->interval->y;
|
||||
}
|
||||
elseif ( $term->interval->m )
|
||||
{
|
||||
$billingCycle['frequency']['interval_unit'] = 'MONTH';
|
||||
$billingCycle['frequency']['interval_count'] = $term->interval->m;
|
||||
}
|
||||
elseif ( $term->interval->d )
|
||||
{
|
||||
if ( $term->interval->d % 7 == 0 )
|
||||
{
|
||||
$billingCycle['frequency']['interval_unit'] = 'WEEK';
|
||||
$billingCycle['frequency']['interval_count'] = $term->interval->d / 7;
|
||||
}
|
||||
else
|
||||
{
|
||||
$billingCycle['frequency']['interval_unit'] = 'DAY';
|
||||
$billingCycle['frequency']['interval_count'] = $term->interval->d;
|
||||
}
|
||||
}
|
||||
|
||||
/* Create a plan */
|
||||
$planDetails = array(
|
||||
'product_id' => $product['id'],
|
||||
'name' => $title,
|
||||
'description' => $description,
|
||||
'billing_cycles' => array(),
|
||||
'billing_cycles' => $billingCycles,
|
||||
'payment_preferences' => array(
|
||||
'auto_bill_outstanding' => FALSE,
|
||||
'payment_failure_threshold' => \intval( $settings['billing_agreement_allowed_fails'] )
|
||||
@@ -541,28 +595,7 @@ class _PayPal extends \IPS\nexus\Gateway
|
||||
),
|
||||
'quantity_supported' => FALSE
|
||||
);
|
||||
if ( $transaction->amount->amount->compare( $renewalAmountIncludingTax ) === 0 )
|
||||
{
|
||||
$planDetails['billing_cycles'][] = $billingCycle;
|
||||
}
|
||||
else
|
||||
{
|
||||
$planDetails['billing_cycles'][] = array(
|
||||
'pricing_scheme' => array(
|
||||
'fixed_price' => array(
|
||||
'currency_code' => $transaction->amount->currency,
|
||||
'value' => $transaction->amount->amountAsString()
|
||||
),
|
||||
),
|
||||
'frequency' => $billingCycle['frequency'],
|
||||
'tenure_type' => 'TRIAL',
|
||||
'sequence' => 1,
|
||||
'total_cycles' => 1
|
||||
);
|
||||
|
||||
$billingCycle['sequence'] = 2;
|
||||
$planDetails['billing_cycles'][] = $billingCycle;
|
||||
}
|
||||
|
||||
$plan = $this->api( 'billing/plans', $planDetails );
|
||||
|
||||
/* Create a subscription */
|
||||
@@ -611,12 +644,18 @@ class _PayPal extends \IPS\nexus\Gateway
|
||||
*/
|
||||
public function void( \IPS\nexus\Transaction $transaction )
|
||||
{
|
||||
/* If this is is the intitial transaction for a billing agreement which hasn't
|
||||
/* If this is the initial transaction for a billing agreement which hasn't
|
||||
been processed yet, cancel the billing agreement. If it's a subscription it may still be the first payment */
|
||||
if ( $transaction->status === $transaction::STATUS_GATEWAY_PENDING and $transaction->billing_agreement )
|
||||
{
|
||||
$transaction->billing_agreement->cancel();
|
||||
}
|
||||
|
||||
/* If the transaction is not in a 'hold' state, it cannot be voided and must be refunded instead */
|
||||
if( !\in_array( $transaction->status, array( $transaction::STATUS_HELD, $transaction::STATUS_REVIEW ) ) || !$transaction->auth )
|
||||
{
|
||||
return $this->refund( $transaction );
|
||||
}
|
||||
|
||||
/* Try to find the authorization ID */
|
||||
if ( mb_substr( $transaction->gw_id, 0, 4 ) === 'PAY-' )
|
||||
@@ -646,33 +685,26 @@ class _PayPal extends \IPS\nexus\Gateway
|
||||
}
|
||||
else
|
||||
{
|
||||
$authId = $transaction->gw_id;
|
||||
if( $transaction->billing_agreement )
|
||||
{
|
||||
try
|
||||
{
|
||||
$sale = $this->api( "payments/sale/{$transaction->gw_id}", NULL, 'get' );
|
||||
return $this->refund( $transaction ); // "Sales" came from Billing Agreements and have already been captured
|
||||
return $this->refund( $transaction );
|
||||
}
|
||||
catch ( \IPS\nexus\Gateway\PayPal\Exception $e )
|
||||
{
|
||||
$authId = $transaction->gw_id;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$authId = $transaction->gw_id;
|
||||
catch ( \IPS\nexus\Gateway\PayPal\Exception $e ){}
|
||||
}
|
||||
}
|
||||
|
||||
/* Void it */
|
||||
return $this->api( "payments/authorization/{$authId}/void" );
|
||||
return $this->api( "payments/authorizations/{$authId}/void", null, 'POST', TRUE, NULL, NULL, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Capture
|
||||
*
|
||||
* @param \IPS\nexus\Transaction $transaction Transaction
|
||||
* @return void
|
||||
* @return bool
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function capture( \IPS\nexus\Transaction $transaction )
|
||||
@@ -757,45 +789,34 @@ class _PayPal extends \IPS\nexus\Gateway
|
||||
}
|
||||
else
|
||||
{
|
||||
$authId = $transaction->gw_id;
|
||||
if( $transaction->billing_agreement )
|
||||
{
|
||||
try
|
||||
{
|
||||
$sale = $this->api( "payments/sale/{$transaction->gw_id}", NULL, 'get' );
|
||||
$sale = $this->api( "payments/authorizations/{$authId}", NULL, 'get', TRUE, NULL, NULL, 2 );
|
||||
return TRUE; // "Sales" came from Billing Agreements and have already been captured
|
||||
}
|
||||
catch ( \IPS\nexus\Gateway\PayPal\Exception $e )
|
||||
{
|
||||
$authId = $transaction->gw_id;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$authId = $transaction->gw_id;
|
||||
catch ( \IPS\nexus\Gateway\PayPal\Exception $e ){}
|
||||
}
|
||||
}
|
||||
|
||||
/* Capture it */
|
||||
try
|
||||
{
|
||||
$response = $this->api( "payments/authorization/{$authId}/capture", array(
|
||||
'amount' => array(
|
||||
'currency' => $transaction->amount->currency,
|
||||
'total' => $transaction->amount->amountAsString(),
|
||||
),
|
||||
'is_final_capture' => TRUE,
|
||||
) );
|
||||
$response = $this->api( "payments/authorizations/{$authId}/capture", NULL, 'POST', TRUE, NULL, NULL, 2 );
|
||||
$transaction->gw_id = $response['id']; // We now set the gateway ID to the capture ID
|
||||
$transaction->save();
|
||||
}
|
||||
catch ( \IPS\nexus\Gateway\PayPal\Exception $e )
|
||||
{
|
||||
if ( $e->getName() == 'AUTHORIZATION_ALREADY_COMPLETED' )
|
||||
if ( $e->getName() == 'ORDER_ALREADY_AUTHORIZED' )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -816,7 +837,7 @@ class _PayPal extends \IPS\nexus\Gateway
|
||||
$amount = $amount ? new \IPS\nexus\Money( $amount, $transaction->currency ) : $transaction->amount;
|
||||
|
||||
/* If it's a billing agreement and the gateway ID isn't prefixed with an I, it's likely to be a subscription */
|
||||
if( $transaction->billing_agreement AND $transaction->gw_id AND mb_substr( $transaction->gw_id, 0, 2 ) !== 'I-' )
|
||||
if( $transaction->billing_agreement AND $transaction->gw_id AND mb_substr( $transaction->billing_agreement->gw_id, 0, 2 ) !== 'I-' )
|
||||
{
|
||||
$response = $this->api( "payments/sale/{$transaction->gw_id}/refund", array( 'amount' => array(
|
||||
'currency' => $amount->currency,
|
||||
@@ -855,10 +876,13 @@ class _PayPal extends \IPS\nexus\Gateway
|
||||
}
|
||||
|
||||
/* Process Refund */
|
||||
$response = $this->api( "payments/capture/{$captureId}/refund", array( 'amount' => array(
|
||||
'currency' => $amount->currency,
|
||||
'total' => $amount->amountAsString()
|
||||
) ) );
|
||||
$response = $this->api( "payments/captures/{$captureId}/refund", array(
|
||||
'amount' => array(
|
||||
'currency_code' => $amount->currency,
|
||||
'value' => $amount->amountAsString()
|
||||
),
|
||||
'invoice_id' => $transaction->invoice->id
|
||||
), 'POST', TRUE, NULL, NULL, 2 );
|
||||
return $response['id'];
|
||||
}
|
||||
|
||||
@@ -884,9 +908,10 @@ class _PayPal extends \IPS\nexus\Gateway
|
||||
public function settings( &$form )
|
||||
{
|
||||
$settings = json_decode( $this->settings, TRUE );
|
||||
|
||||
|
||||
if ( isset( $settings['type'] ) and $settings['type'] === 'card' )
|
||||
{
|
||||
$form->addMessage( \IPS\Member::loggedIn()->language()->addToStack( 'gateway_deprecated', FALSE, array( 'sprintf' => 'PayPal Credit Card' ) ), 'ipsMessage ipsMessage_warning' );
|
||||
$form->add( new \IPS\Helpers\Form\Radio( 'paypal_type', $settings['type'], TRUE, array( 'options' => array( 'paypal' => 'paypal_type_paypal', 'card' => 'paypal_type_card' ), 'toggles' => array( 'paypal' => array( 'paypal_billing_agreements' ), 'card' => array( 'paypal_vault' ) ) ) ) );
|
||||
}
|
||||
|
||||
@@ -911,12 +936,12 @@ class _PayPal extends \IPS\nexus\Gateway
|
||||
}, NULL, NULL, 'paypal_billing_agreements' ) );
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Number( 'paypal_billing_agreement_allowed_fails', ( $this->id AND isset( $settings['billing_agreement_allowed_fails'] ) ) ? $settings['billing_agreement_allowed_fails'] : 0, FALSE, array( 'unlimited' => 0, 'min' => 1 ), NULL, \IPS\Member::loggedIn()->language()->addToStack('paypal_billing_agreement_allowed_fails_prefix'), \IPS\Member::loggedIn()->language()->addToStack('paypal_billing_agreement_allowed_fails_suffix'), 'paypal_billing_agreement_allowed_fails' ) );
|
||||
|
||||
|
||||
if ( isset( $settings['type'] ) and $settings['type'] === 'card' )
|
||||
{
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'paypal_vault', ( $this->id and isset( $settings['vault'] ) ) ? $settings['vault'] : TRUE, FALSE, array(), NULL, NULL, NULL, 'paypal_vault' ) );
|
||||
}
|
||||
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'paypal_client_id', $settings['client_id'], TRUE ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'paypal_secret', $settings['secret'], TRUE ) );
|
||||
}
|
||||
@@ -989,11 +1014,13 @@ class _PayPal extends \IPS\nexus\Gateway
|
||||
* @param array $data The data to send
|
||||
* @param string $method Method (get/post)
|
||||
* @param array|NULL $settings Settings (NULL for saved setting)
|
||||
* @param string|Null $requestId
|
||||
* @param int|Null $version API Version
|
||||
* @return array|null
|
||||
* @throws \IPS\Http|Exception
|
||||
* @throws \IPS\nexus\Gateway\PayPal\Exception
|
||||
*/
|
||||
public function api( $uri, $data=NULL, $method='post', $expectResponse=TRUE, $settings=NULL )
|
||||
public function api( $uri, $data=NULL, $method='post', $expectResponse=TRUE, $settings=NULL, $requestId=NULL, $version=NULL )
|
||||
{
|
||||
if ( !$settings )
|
||||
{
|
||||
@@ -1007,14 +1034,18 @@ class _PayPal extends \IPS\nexus\Gateway
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
|
||||
/* Some API calls (e.g. for billing agreements) still use v1 for the REST APIs */
|
||||
$version = $version ?: 1;
|
||||
|
||||
$response = \IPS\Http\Url::external( 'https://' . ( \IPS\NEXUS_TEST_GATEWAYS ? 'api.sandbox.paypal.com' : 'api.paypal.com' ) . '/v1/' . $uri )
|
||||
$response = \IPS\Http\Url::external( 'https://' . ( \IPS\NEXUS_TEST_GATEWAYS ? 'api-m.sandbox.paypal.com' : 'api-m.paypal.com' ) . '/v' . $version . '/' . $uri )
|
||||
->request( \IPS\LONG_REQUEST_TIMEOUT )
|
||||
->forceTls()
|
||||
->setHeaders( array(
|
||||
'Content-Type' => 'application/json',
|
||||
'Authorization' => "Bearer {$settings['token']}",
|
||||
'PayPal-Partner-Attribution-Id' => 'InvisionPower_SP'
|
||||
'PayPal-Partner-Attribution-Id' => 'InvisionPower_SP',
|
||||
'PayPal-Request-Id' => $requestId
|
||||
) )
|
||||
->$method( $data === NULL ? NULL : json_encode( $data ) );
|
||||
|
||||
@@ -1038,14 +1069,14 @@ class _PayPal extends \IPS\nexus\Gateway
|
||||
*
|
||||
* @param array|NULL $settings Settings (NULL for saved setting)
|
||||
* @return array
|
||||
* @throws \IPS\Http|Exception
|
||||
* @throws \IPS\Http\Request\Exception
|
||||
* @throws \UnexpectedValueException
|
||||
*/
|
||||
protected function getNewToken( $settings = NULL )
|
||||
{
|
||||
$settings = $settings ?: json_decode( $this->settings, TRUE );
|
||||
|
||||
$response = \IPS\Http\Url::external( 'https://' . ( \IPS\NEXUS_TEST_GATEWAYS ? 'api.sandbox.paypal.com' : 'api.paypal.com' ) . '/v1/oauth2/token' )
|
||||
$response = \IPS\Http\Url::external( 'https://' . ( \IPS\NEXUS_TEST_GATEWAYS ? 'api-m.sandbox.paypal.com' : 'api-m.paypal.com' ) . '/v1/oauth2/token' )
|
||||
->request()
|
||||
->forceTls()
|
||||
->setHeaders( array(
|
||||
@@ -1063,14 +1094,63 @@ class _PayPal extends \IPS\nexus\Gateway
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the PayPal exception and mark the transaction as failed
|
||||
*
|
||||
* @param \IPS\nexus\Transaction $transaction
|
||||
* @param \Exception $e
|
||||
* @return void
|
||||
*/
|
||||
public function processException( \IPS\nexus\Transaction $transaction, \Exception $e )
|
||||
{
|
||||
/* Make sure it's a PayPal exception only. The interface might send us a different exception type */
|
||||
if( ! ( $e instanceof \IPS\nexus\Gateway\PayPal\Exception ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Mark the transaction as failed */
|
||||
$transaction->status = \IPS\nexus\Transaction::STATUS_REFUSED;
|
||||
|
||||
$details = json_decode( $e->extraLogData(), true );
|
||||
|
||||
/* Log the error message to the transaction history */
|
||||
$extra = $transaction->extra;
|
||||
$extra['history'][] = array( 's' => \IPS\nexus\Transaction::STATUS_REFUSED, 'on' => time(), 'noteRaw' => ( $details['details'][0]['description'] ?? $e->getMessage() ) );
|
||||
|
||||
if( isset( $details['processor_response'] ) )
|
||||
{
|
||||
$extra['processor_response'] = $details['processor_response'];
|
||||
|
||||
// log this failure to the customer history
|
||||
$responseCode = $details['processor_response']['response_code'];
|
||||
if( \is_numeric( $responseCode ) )
|
||||
{
|
||||
$responseCode = (int) $responseCode;
|
||||
}
|
||||
if( $transaction->member->language()->checkKeyExists( 'processor_response_code__' . $responseCode ) )
|
||||
{
|
||||
$failureReason = $transaction->member->language()->get( 'processor_response_code__' . $responseCode );
|
||||
$transaction->member->logHistory( 'nexus', 'custom', array(
|
||||
'message' => \sprintf( $transaction->member->language()->get( 'history_payment_rejected' ), $failureReason )
|
||||
) );
|
||||
}
|
||||
}
|
||||
|
||||
$transaction->extra = $extra;
|
||||
$transaction->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get address for PayPal
|
||||
*
|
||||
* @param \IPS\nexus\Transaction $transaction Transaction
|
||||
* @param \IPS\nexus\Customer $customer
|
||||
* @param string $paymentType
|
||||
* @return array
|
||||
*/
|
||||
protected function _getAddress( \IPS\GeoLocation $address, \IPS\nexus\Customer $customer )
|
||||
protected function _getAddress( \IPS\GeoLocation $address, \IPS\nexus\Customer $customer, $paymentType='paypal' )
|
||||
{
|
||||
/* PayPal requires short codes for states */
|
||||
$state = $address->region;
|
||||
@@ -1085,21 +1165,41 @@ class _PayPal extends \IPS\nexus\Gateway
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Construct */
|
||||
$address = array(
|
||||
'line1' => $address->addressLines[0],
|
||||
'line2' => isset( $address->addressLines[1] ) ? $address->addressLines[1] : '',
|
||||
'city' => $address->city,
|
||||
'country_code' => $address->country,
|
||||
'postal_code' => $address->postalCode,
|
||||
'state' => $state,
|
||||
);
|
||||
|
||||
/* Add phone number */
|
||||
if ( $customer->cm_phone )
|
||||
if( $paymentType == 'card' )
|
||||
{
|
||||
$address['phone'] = preg_replace( '/[^\+0-9\s]/', '', $customer->cm_phone );
|
||||
/* Construct */
|
||||
$address = array(
|
||||
'line1' => $address->addressLines[0],
|
||||
'line2' => isset( $address->addressLines[1] ) ? $address->addressLines[1] : '',
|
||||
'city' => $address->city,
|
||||
'country_code' => $address->country,
|
||||
'postal_code' => $address->postalCode,
|
||||
'state' => $state,
|
||||
);
|
||||
|
||||
/* Add phone number */
|
||||
if ( $customer->cm_phone )
|
||||
{
|
||||
$address['phone'] = preg_replace( '/[^\+0-9\s]/', '', $customer->cm_phone );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Construct */
|
||||
$address = array(
|
||||
'address_line_1' => $address->addressLines[0],
|
||||
'address_line_2' => isset( $address->addressLines[1] ) ? $address->addressLines[1] : '',
|
||||
'admin_area_2' => $address->city,
|
||||
'country_code' => $address->country,
|
||||
'postal_code' => $address->postalCode,
|
||||
'admin_area_1' => $state,
|
||||
);
|
||||
|
||||
if( empty( $address['address_line_2'] ) )
|
||||
{
|
||||
unset( $address['address_line_2'] );
|
||||
}
|
||||
}
|
||||
|
||||
/* Return */
|
||||
@@ -1110,7 +1210,7 @@ class _PayPal extends \IPS\nexus\Gateway
|
||||
* Get first name for PayPal
|
||||
*
|
||||
* @param \IPS\nexus\Transaction $transaction Transaction
|
||||
* @return array
|
||||
* @return string
|
||||
*/
|
||||
protected function _getFirstName( \IPS\nexus\Transaction $transaction )
|
||||
{
|
||||
@@ -1121,12 +1221,102 @@ class _PayPal extends \IPS\nexus\Gateway
|
||||
* Get last name for PayPal
|
||||
*
|
||||
* @param \IPS\nexus\Transaction $transaction Transaction
|
||||
* @return array
|
||||
* @return string
|
||||
*/
|
||||
protected function _getLastName( \IPS\nexus\Transaction $transaction )
|
||||
{
|
||||
return $transaction->invoice->member->member_id ? $transaction->invoice->member->cm_last_name : $transaction->invoice->guest_data['member']['cm_last_name'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get transaction data for PayPal
|
||||
*
|
||||
* @param \IPS\nexus\Transaction $transaction Transaction
|
||||
* @return array
|
||||
*/
|
||||
protected function _getPurchaseUnits( \IPS\nexus\Transaction $transaction )
|
||||
{
|
||||
/* Init */
|
||||
$payPalTransactionData = array(
|
||||
'amount' => array(
|
||||
'currency_code' => $transaction->amount->currency,
|
||||
'value' => $transaction->amount->amountAsString(),
|
||||
),
|
||||
'custom_id'=> \IPS\Settings::i()->site_secret_key . '-' . $transaction->id,
|
||||
'invoice_id' => $transaction->invoice->id,
|
||||
'items' => array()
|
||||
);
|
||||
|
||||
/* If we're paying the whole invoice, we can add item data... */
|
||||
if ( $transaction->amount->amount->compare( $transaction->invoice->total->amount ) === 0 )
|
||||
{
|
||||
$summary = $transaction->invoice->summary();
|
||||
|
||||
/* Shipping / Tax */
|
||||
$payPalTransactionData['amount']['breakdown'] = array(
|
||||
'shipping' => array( 'currency_code' => $summary['shippingTotal']->currency, 'value' => $summary['shippingTotal']->amountAsString() ),
|
||||
'item_total' => array( 'currency_code' => $summary['subtotal']->currency, 'value' => $summary['subtotal']->amountAsString() ),
|
||||
'tax_total' => array( 'currency_code' => $summary['taxTotal']->currency, 'value' => $summary['taxTotal']->amountAsString() ),
|
||||
'discount' => array( 'currency_code' => $summary['discount']->currency, 'value' => $summary['discount']->amountAsString() )
|
||||
);
|
||||
|
||||
/* Items */
|
||||
$itemTotal = new \IPS\Math\Number( '0' );
|
||||
foreach ( $summary['items'] as $item )
|
||||
{
|
||||
if( $item instanceof \IPS\nexus\extensions\nexus\Item\CouponDiscount )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$itemCategory = 'DIGITAL_GOODS';
|
||||
if( $item->physical )
|
||||
{
|
||||
$itemCategory = 'PHYSICAL_GOODS';
|
||||
}
|
||||
elseif( $item instanceof \IPS\nexus\extensions\nexus\Item\Donation )
|
||||
{
|
||||
$itemCategory = 'DONATION';
|
||||
}
|
||||
|
||||
$itemData = array(
|
||||
'name' => mb_strlen( $item->name ) > 127 ? mb_substr( $item->name, 0, 124 ) . '...' : $item->name,
|
||||
'quantity' => $item->quantity,
|
||||
'unit_amount' => array(
|
||||
'currency_code' => $item->price->currency,
|
||||
'value' => $item->price->amountAsString()
|
||||
),
|
||||
'category' => $itemCategory
|
||||
);
|
||||
$itemTotal = $itemTotal->add( $item->price->amount->multiply( new \IPS\Math\Number("{$item->quantity}") ) );
|
||||
|
||||
$payPalTransactionData['items'][] = $itemData;
|
||||
}
|
||||
|
||||
/* PayPal requires that the item total be equal to the pre-discounted amounts. The summary uses the invoice total, which is not accepted. */
|
||||
$itemTotal = new \IPS\nexus\Money( $itemTotal, $transaction->amount->currency );
|
||||
$payPalTransactionData['amount']['breakdown']['item_total']['value'] = $itemTotal->amountAsString();
|
||||
|
||||
/* Shipping Address */
|
||||
if ( $transaction->invoice->shipaddress )
|
||||
{
|
||||
$payPalTransactionData['shipping'] = array(
|
||||
'address' => $this->_getAddress( $transaction->invoice->shipaddress, $transaction->member ),
|
||||
'name' => array(
|
||||
'full_name' => $this->_getFirstName( $transaction ) . ' ' . $this->_getLastName( $transaction )
|
||||
),
|
||||
'type' => 'SHIPPING'
|
||||
);
|
||||
}
|
||||
}
|
||||
/* Otherwise just use a generic description */
|
||||
else
|
||||
{
|
||||
$payPalTransactionData['description'] = sprintf( $transaction->member->language()->get('partial_payment_desc'), $transaction->invoice->id );
|
||||
}
|
||||
|
||||
return array( $payPalTransactionData );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get transaction data for PayPal
|
||||
@@ -1174,7 +1364,7 @@ class _PayPal extends \IPS\nexus\Gateway
|
||||
{
|
||||
$payPalTransactionData['item_list']['shipping_address'] = array_merge(
|
||||
array( 'recipient_name' => $this->_getFirstName( $transaction ) . ' ' . $this->_getLastName( $transaction ) ),
|
||||
$this->_getAddress( $transaction->invoice->shipaddress, $transaction->invoice->member )
|
||||
$this->_getAddress( $transaction->invoice->shipaddress, $transaction->invoice->member, 'card' )
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1190,24 +1380,86 @@ class _PayPal extends \IPS\nexus\Gateway
|
||||
/**
|
||||
* Can we handle the renewal terms in a billing agreement?
|
||||
*
|
||||
* @param array $recurrings Details about recurring costs
|
||||
* @return array
|
||||
* @param array $recurrings Details about recurring costs
|
||||
* @return bool
|
||||
*/
|
||||
protected static function _canProcessRecurringsAsBillingAgreement( array $recurrings )
|
||||
{
|
||||
if ( \count( $recurrings ) == 1 )
|
||||
if( \count( $recurrings ) == 1 )
|
||||
{
|
||||
$recurrance = array_pop( $recurrings );
|
||||
|
||||
/* We need to allow for some minor deviation than checking for the exact second, this can happen if the request is at just the exact time where
|
||||
the clock ticks over to the next second */
|
||||
$difference = \IPS\DateTime::create()->add( $recurrance['term']->interval )->getTimestamp() - $recurrance['dueDate']->getTimestamp();
|
||||
if ( $difference >= 0 AND $difference < 10 )
|
||||
/* If we only have one item, we're fine */
|
||||
if( \count( $recurrance['items'] ) == 1 )
|
||||
{
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Make sure all the terms match */
|
||||
$initialTermFrequency = null;
|
||||
foreach( $recurrance['items'] as $item )
|
||||
{
|
||||
if( isset( $item->initialInterval ) AND $item->initialInterval instanceof \DateInterval )
|
||||
{
|
||||
$thisTermFrequency = static::_getFrequencyFromInterval( $item->initialInterval );
|
||||
if( $initialTermFrequency !== null AND ( $thisTermFrequency['interval_unit'] != $initialTermFrequency['interval_unit'] OR $thisTermFrequency['interval_count'] != $initialTermFrequency['interval_count'] ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
$initialTermFrequency = $thisTermFrequency;
|
||||
}
|
||||
elseif( $initialTermFrequency !== null )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the billing cycle frequency based on the DateInterval
|
||||
*
|
||||
* @param \DateInterval $term
|
||||
* @return array|null
|
||||
*/
|
||||
protected static function _getFrequencyFromInterval( \DateInterval $term )
|
||||
{
|
||||
if( $term->y )
|
||||
{
|
||||
return array(
|
||||
'interval_unit' => 'YEAR',
|
||||
'interval_count' => $term->y
|
||||
);
|
||||
}
|
||||
|
||||
if( $term->m )
|
||||
{
|
||||
return array(
|
||||
'interval_unit' => 'MONTH',
|
||||
'interval_count' => $term->m
|
||||
);
|
||||
}
|
||||
|
||||
if( $term->d )
|
||||
{
|
||||
if( $term->d % 7 == 0 )
|
||||
{
|
||||
return array(
|
||||
'interval_unit' => 'WEEK',
|
||||
'interval_count' => $term->d / 7
|
||||
);
|
||||
}
|
||||
|
||||
return array(
|
||||
'interval_unit' => 'DAY',
|
||||
'interval_count' => $term->d
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,9 @@
|
||||
namespace IPS\nexus\Gateway\PayPal;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
|
||||
use IPS\Settings;
|
||||
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
@@ -33,9 +36,8 @@ class _Payout extends \IPS\nexus\Payout
|
||||
$settings = json_decode( \IPS\Settings::i()->nexus_payout, TRUE );
|
||||
|
||||
$return = array();
|
||||
$return[] = new \IPS\Helpers\Form\Text( 'paypal_api_username', isset( $settings['PayPal'] ) ? $settings['PayPal']['api_username'] : '' );
|
||||
$return[] = new \IPS\Helpers\Form\Text( 'paypal_api_password', isset( $settings['PayPal'] ) ? $settings['PayPal']['api_password'] : '' );
|
||||
$return[] = new \IPS\Helpers\Form\Text( 'paypal_api_signature', isset( $settings['PayPal'] ) ? $settings['PayPal']['api_signature'] : '' );
|
||||
$return[] = new \IPS\Helpers\Form\Text( 'paypal_client_id', ( isset( $settings['PayPal']['client_id'] ) AND isset( $settings['PayPal']['client_id'] ) AND $settings['PayPal']['client_id'] ) ? $settings['PayPal']['client_id'] : '', NULL );
|
||||
$return[] = new \IPS\Helpers\Form\Text( 'paypal_secret', ( isset( $settings['PayPal']['secret'] ) AND $settings['PayPal']['secret'] ) ? $settings['PayPal']['secret'] : '', NULL );
|
||||
return $return;
|
||||
}
|
||||
|
||||
@@ -77,28 +79,60 @@ class _Payout extends \IPS\nexus\Payout
|
||||
*/
|
||||
public function process()
|
||||
{
|
||||
$settings = json_decode( \IPS\Settings::i()->nexus_payout, TRUE );
|
||||
|
||||
$response = \IPS\Http\Url::external( \IPS\NEXUS_TEST_GATEWAYS ? 'https://api-3t.sandbox.paypal.com/nvp' : 'https://api-3t.paypal.com/nvp' )->request()->post( array(
|
||||
'METHOD' => 'MassPay',
|
||||
'VERSION' => '90',
|
||||
'USER' => $settings['PayPal']['api_username'],
|
||||
'PWD' => $settings['PayPal']['api_password'],
|
||||
'SIGNATURE' => $settings['PayPal']['api_signature'],
|
||||
'RECEIVERTYPE' => 'EmailAddress',
|
||||
'CURRENCYCODE' => $this->amount->currency,
|
||||
'L_EMAIL0' => $this->data,
|
||||
'L_AMT0' => $this->amount->amountAsString()
|
||||
) )->decodeQueryString();
|
||||
static::checkToken();
|
||||
|
||||
if ( $response['ACK'] != 'Success' )
|
||||
$settings = json_decode( \IPS\Settings::i()->nexus_payout, true );
|
||||
|
||||
$data = array(
|
||||
'items' => array(
|
||||
array(
|
||||
'amount' => array(
|
||||
'currency' => $this->amount->currency,
|
||||
'value' => $this->amount->amountAsString()
|
||||
),
|
||||
'receiver' => $this->data,
|
||||
'recipient_type' => 'EMAIL'
|
||||
)
|
||||
),
|
||||
'sender_batch_header' => array(
|
||||
'recipient_type' => 'EMAIL',
|
||||
'sender_batch_id' => "Payout " . \IPS\DateTime::create()->rfc3339()
|
||||
)
|
||||
);
|
||||
|
||||
$response = \IPS\Http\Url::external( 'https://' . ( \IPS\NEXUS_TEST_GATEWAYS ? 'api-m.sandbox.paypal.com' : 'api.paypal.com' ) . '/v1/payments/payouts' )
|
||||
->request()
|
||||
->forceTls()
|
||||
->setHeaders( array(
|
||||
'Content-Type' => 'application/json',
|
||||
'Authorization' => 'Bearer ' . $settings['PayPal']['token']
|
||||
) )
|
||||
->post( json_encode( $data ) );
|
||||
|
||||
$json = $response->decodeJson();
|
||||
|
||||
if( $response->httpResponseCode != 201 )
|
||||
{
|
||||
throw new \DomainException( $response['L_LONGMESSAGE0'] );
|
||||
throw new \IPS\Http\Request\Exception( $json['message'] );
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->status = static::STATUS_COMPLETE;
|
||||
$this->completed = new \IPS\DateTime;
|
||||
|
||||
if( isset( $json['batch_header'] ) AND $json['batch_header']['payout_batch_id'] )
|
||||
{
|
||||
$this->gw_id = $json['batch_header']['payout_batch_id'];
|
||||
switch( $json['batch_header']['batch_status'] )
|
||||
{
|
||||
case 'DENIED':
|
||||
case 'CANCELED':
|
||||
$this->status = static::STATUS_CANCELED;
|
||||
break;
|
||||
case 'SUCCESS':
|
||||
$this->status = static::STATUS_COMPLETE;
|
||||
break;
|
||||
default:
|
||||
$this->status = static::STATUS_PROCESSING;
|
||||
break;
|
||||
}
|
||||
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
@@ -112,57 +146,167 @@ class _Payout extends \IPS\nexus\Payout
|
||||
*/
|
||||
public static function massProcess( \IPS\Patterns\ActiveRecordIterator $payouts )
|
||||
{
|
||||
/* Make sure we check the token first so that we have proper authorization to API calls */
|
||||
static::checkToken();
|
||||
|
||||
$settings = json_decode( \IPS\Settings::i()->nexus_payout, TRUE );
|
||||
|
||||
$byCurrency = array();
|
||||
foreach ( $payouts as $payout )
|
||||
/* Build a batch of payout items */
|
||||
$payoutIds = $payoutData = [];
|
||||
foreach( $payouts as $payout )
|
||||
{
|
||||
$byCurrency[ $payout->amount->currency ][] = array( 'email' => $payout->data, 'amount' => $payout->amount->amountAsString() );
|
||||
$payoutIds[ $payout->amount->currency ] = $payout->id;
|
||||
$payoutData[ $payout->amount->currency ][] = [
|
||||
'amount' => [
|
||||
'currency' => $payout->amount->currency,
|
||||
'value' => $payout->amount->amountAsString()
|
||||
],
|
||||
'receiver' => $payout->data,
|
||||
'recipient_type' => 'EMAIL'
|
||||
];
|
||||
}
|
||||
|
||||
$batches = array();
|
||||
foreach ( $byCurrency as $currency => $recipients )
|
||||
|
||||
foreach( $payoutData as $currency => $batchData )
|
||||
{
|
||||
$baseData = array(
|
||||
'METHOD' => 'MassPay',
|
||||
'VERSION' => '90',
|
||||
'USER' => $settings['PayPal']['api_username'],
|
||||
'PWD' => $settings['PayPal']['api_password'],
|
||||
'SIGNATURE' => $settings['PayPal']['api_signature'],
|
||||
'RECEIVERTYPE' => 'EmailAddress',
|
||||
'CURRENCYCODE' => $currency,
|
||||
);
|
||||
|
||||
$data = $baseData;
|
||||
foreach ( $recipients as $i => $recipient )
|
||||
$requestData = [
|
||||
'items' => $batchData,
|
||||
'sender_batch_header' => [
|
||||
'recipient_type' => 'EMAIL',
|
||||
'sender_batch_id' => "Payout-{$currency}-" . \IPS\DateTime::create()->rfc3339()
|
||||
]
|
||||
];
|
||||
|
||||
$response = \IPS\Http\Url::external( 'https://' . ( \IPS\NEXUS_TEST_GATEWAYS ? 'api-m.sandbox.paypal.com' : 'api.paypal.com' ) . '/v1/payments/payouts' )
|
||||
->request()
|
||||
->forceTls()
|
||||
->setHeaders( [
|
||||
'Content-Type' => 'application/json',
|
||||
'Authorization' => 'Bearer ' . $settings['PayPal']['token']
|
||||
] )
|
||||
->post( json_encode( $requestData ) );
|
||||
|
||||
if( $response->httpResponseCode != 201 )
|
||||
{
|
||||
$data["L_EMAIL{$i}"] = $recipient['email'];
|
||||
$data["L_AMT{$i}"] = $recipient['amount'];
|
||||
|
||||
if ( $i >= 249 )
|
||||
throw new \RuntimeException( (string) $response, $response->httpResponseCode );
|
||||
}
|
||||
|
||||
$response = $response->decodeJson();
|
||||
|
||||
if( isset( $response['batch_header'] ) AND $response['batch_header']['payout_batch_id'] )
|
||||
{
|
||||
$update = [
|
||||
'po_gw_id' => $response['batch_header']['payout_batch_id']
|
||||
];
|
||||
|
||||
switch( $response['batch_header']['batch_status'] )
|
||||
{
|
||||
$batches[] = $data;
|
||||
$data = $baseData;
|
||||
case 'DENIED':
|
||||
case 'CANCELED':
|
||||
$update['po_status'] = static::STATUS_CANCELED;
|
||||
break;
|
||||
case 'SUCCESS':
|
||||
$update['po_status'] = static::STATUS_COMPLETE;
|
||||
break;
|
||||
default:
|
||||
$update['po_status'] = static::STATUS_PROCESSING;
|
||||
break;
|
||||
}
|
||||
|
||||
\IPS\Db::i()->update( 'nexus_payouts', $update, \IPS\Db::i()->in( 'po_id', $payoutIds[ $currency ] ) );
|
||||
}
|
||||
|
||||
$batches[] = $data;
|
||||
}
|
||||
|
||||
$error = NULL;
|
||||
foreach ( $batches as $batchData )
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief cache batch results for further lookup
|
||||
*/
|
||||
protected static array $_batchCache = [];
|
||||
|
||||
/**
|
||||
* Check the status of a payout batch and update the withdrawal requests accordingly
|
||||
*
|
||||
* @param string $batchId
|
||||
* @return string|NULL
|
||||
*/
|
||||
public static function checkStatus( string $batchId ):? string
|
||||
{
|
||||
if( isset( static::$_batchCache[ $batchId ] ) )
|
||||
{
|
||||
$response = \IPS\Http\Url::external( \IPS\NEXUS_TEST_GATEWAYS ? 'https://api-3t.sandbox.paypal.com/nvp' : 'https://api-3t.paypal.com/nvp' )->request()->post( $batchData )->decodeQueryString();
|
||||
|
||||
if ( $response['ACK'] != 'Success' )
|
||||
return static::$_batchCache[ $batchId ];
|
||||
}
|
||||
|
||||
/* Make sure we check the token first so that we have proper authorization to API calls */
|
||||
static::checkToken();
|
||||
|
||||
$settings = json_decode( \IPS\Settings::i()->nexus_payout, TRUE );
|
||||
|
||||
$response = \IPS\Http\Url::external( 'https://' . ( \IPS\NEXUS_TEST_GATEWAYS ? 'api-m.sandbox.paypal.com' : 'api.paypal.com' ) . '/v1/payments/payouts/' . $batchId )
|
||||
->request()
|
||||
->forceTls()
|
||||
->setHeaders( array(
|
||||
'Content-Type' => 'application/json',
|
||||
'Authorization' => 'Bearer ' . $settings['PayPal']['token']
|
||||
) )
|
||||
->get()
|
||||
->decodeJson();
|
||||
|
||||
if( isset( $response['batch_header'] ) AND $response['batch_header']['payout_batch_id'] )
|
||||
{
|
||||
switch( $response['batch_header']['batch_status'] )
|
||||
{
|
||||
$error = $response['L_LONGMESSAGE0'];
|
||||
case 'DENIED':
|
||||
case 'CANCELED':
|
||||
return static::$_batchCache[ $batchId ] = static::STATUS_CANCELED;
|
||||
break;
|
||||
case 'SUCCESS':
|
||||
return static::$_batchCache[ $batchId ] = static::STATUS_COMPLETE;
|
||||
break;
|
||||
default:
|
||||
return static::$_batchCache[ $batchId ] = static::STATUS_PROCESSING;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $error )
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Token
|
||||
*
|
||||
* @return void
|
||||
* @throws \IPS\Http\Request\Exception
|
||||
* @throws \UnexpectedValueException
|
||||
*/
|
||||
protected static function checkToken()
|
||||
{
|
||||
$payoutSettings = json_decode( \IPS\Settings::i()->nexus_payout, true );
|
||||
$settings = $payoutSettings['PayPal'];
|
||||
|
||||
if ( !isset( $settings['token'] ) or $settings['token_expire'] < time() )
|
||||
{
|
||||
throw new \DomainException( $error );
|
||||
$response = \IPS\Http\Url::external( 'https://' . ( \IPS\NEXUS_TEST_GATEWAYS ? 'api-m.sandbox.paypal.com' : 'api.paypal.com' ) . '/v1/oauth2/token' )
|
||||
->request()
|
||||
->forceTls()
|
||||
->setHeaders( array(
|
||||
'Accept' => 'application/json',
|
||||
'Accept-Language' => 'en_US',
|
||||
) )
|
||||
->login( $settings['client_id'], $settings['secret'] )
|
||||
->post( array( 'grant_type' => 'client_credentials' ) )
|
||||
->decodeJson();
|
||||
|
||||
if ( !isset( $response['access_token'] ) )
|
||||
{
|
||||
throw new \UnexpectedValueException( isset( $response['error_description'] ) ? $response['error_description'] : $response );
|
||||
}
|
||||
|
||||
$settings['token'] = $response['access_token'];
|
||||
$settings['token_expire'] = ( time() + $response['expires_in'] );
|
||||
$payoutSettings['PayPal'] = $settings;
|
||||
|
||||
\IPS\Settings::i()->changeValues( array(
|
||||
'nexus_payout' => json_encode( $payoutSettings )
|
||||
) );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -781,8 +781,9 @@ class _Stripe extends \IPS\nexus\Gateway
|
||||
|
||||
if ( mb_substr( $transaction->gw_id, 0, 3 ) === 'pi_' )
|
||||
{
|
||||
$response = $this->api( "payment_intents/{$transaction->gw_id}", NULL, 'get' );
|
||||
foreach ( $response['charges']['data'] as $charge )
|
||||
$response = $this->api( "charges?payment_intent={$transaction->gw_id}", NULL, 'get' );
|
||||
|
||||
foreach ( $response['data'] as $charge )
|
||||
{
|
||||
if ( $charge['paid'] )
|
||||
{
|
||||
@@ -829,9 +830,9 @@ class _Stripe extends \IPS\nexus\Gateway
|
||||
{
|
||||
if ( mb_substr( $transaction->gw_id, 0, 3 ) === 'pi_' )
|
||||
{
|
||||
$response = $this->api( "payment_intents/{$transaction->gw_id}", NULL, 'get' );
|
||||
|
||||
foreach ( $response['charges']['data'] as $charge )
|
||||
$response = $this->api( "charges?payment_intent={$transaction->gw_id}", NULL, 'get' );
|
||||
|
||||
foreach ( $response['data'] as $charge )
|
||||
{
|
||||
if ( $charge['paid'] )
|
||||
{
|
||||
@@ -911,8 +912,9 @@ class _Stripe extends \IPS\nexus\Gateway
|
||||
{
|
||||
if ( mb_substr( $transaction->gw_id, 0, 3 ) === 'pi_' )
|
||||
{
|
||||
$response = $this->api( "payment_intents/{$transaction->gw_id}", NULL, 'get' );
|
||||
foreach ( $response['charges']['data'] as $charge )
|
||||
$response = $this->api( "charges?payment_intent={$transaction->gw_id}", NULL, 'get' );
|
||||
|
||||
foreach ( $response['data'] as $charge )
|
||||
{
|
||||
if ( $charge['status'] === 'succeeded' )
|
||||
{
|
||||
@@ -1082,7 +1084,7 @@ class _Stripe extends \IPS\nexus\Gateway
|
||||
|
||||
$response = \IPS\Http\Url::external( 'https://api.stripe.com/v1/' . $uri )
|
||||
->request( \IPS\LONG_REQUEST_TIMEOUT )
|
||||
->setHeaders( array( 'Stripe-Version' => '2019-03-14' ) )
|
||||
->setHeaders( array( 'Stripe-Version' => '2022-11-15' ) )
|
||||
->forceTls()
|
||||
->login( $settings['secret_key'], '' )
|
||||
->$method( $data )
|
||||
|
||||
@@ -1,471 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Hosting Account Model
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
* @package Invision Community
|
||||
* @subpackage Nexus
|
||||
* @since 6 Aug 2014
|
||||
*/
|
||||
|
||||
namespace IPS\nexus\Hosting;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Account Account Model
|
||||
*/
|
||||
class _Account extends \IPS\Patterns\ActiveRecord
|
||||
{
|
||||
/* !ActiveRecord */
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Multiton Store
|
||||
*/
|
||||
protected static $multitons;
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database Table
|
||||
*/
|
||||
public static $databaseTable = 'nexus_hosting_accounts';
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] ID Database Column
|
||||
*/
|
||||
public static $databaseColumnId = 'ps_id';
|
||||
|
||||
/**
|
||||
* Account Types
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function accountTypes()
|
||||
{
|
||||
return array(
|
||||
'Cpanel' => 'IPS\nexus\Hosting\Cpanel\Account',
|
||||
'None' => 'IPS\nexus\Hosting\None\Account',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct ActiveRecord from database row
|
||||
*
|
||||
* @param array $data Row from database table
|
||||
* @param bool $updateMultitonStoreIfExists Replace current object in multiton store if it already exists there?
|
||||
* @return static
|
||||
*/
|
||||
public static function constructFromData( $data, $updateMultitonStoreIfExists = TRUE )
|
||||
{
|
||||
/* Initiate an object */
|
||||
$availableTypes = static::accountTypes();
|
||||
$classname = $availableTypes[ mb_ucfirst( \IPS\nexus\Hosting\Server::load( $data['account_server'] )->type ) ];
|
||||
$obj = new $classname;
|
||||
$obj->_new = FALSE;
|
||||
|
||||
/* Import data */
|
||||
foreach ( $data as $k => $v )
|
||||
{
|
||||
if( static::$databasePrefix )
|
||||
{
|
||||
$k = \substr( $k, \strlen( static::$databasePrefix ) );
|
||||
}
|
||||
|
||||
$obj->_data[ $k ] = $v;
|
||||
}
|
||||
$obj->changed = array();
|
||||
|
||||
/* Return */
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Default Values
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setDefaultValues()
|
||||
{
|
||||
/* Set a random password */
|
||||
$password = '';
|
||||
foreach ( range( 1, 20 ) as $j )
|
||||
{
|
||||
do
|
||||
{
|
||||
$chr = rand( 33, 122 );
|
||||
}
|
||||
while ( \in_array( $chr, array( 38, 43, 47, 64, 92, 93, 94 ) ) );
|
||||
|
||||
$password .= \chr( $chr );
|
||||
}
|
||||
$this->password = $password;
|
||||
|
||||
/* By default the account exists */
|
||||
$this->exists = TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Server
|
||||
*
|
||||
* @param \IPS\nexus\Hosting\Server $server The server
|
||||
* @return void
|
||||
*/
|
||||
public function set_server( \IPS\nexus\Hosting\Server $server )
|
||||
{
|
||||
$this->_data['account_server'] = $server->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Server
|
||||
*
|
||||
* @return \IPS\nexus\Hosting\Server
|
||||
*/
|
||||
public function get_server()
|
||||
{
|
||||
return \IPS\nexus\Hosting\Server::load( $this->_data['account_server'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Domain
|
||||
*
|
||||
* @param string $domain The domain
|
||||
* @return void
|
||||
*/
|
||||
public function set_domain( $domain )
|
||||
{
|
||||
$this->_data['account_domain'] = mb_strtolower( $domain );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Domain
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_domain()
|
||||
{
|
||||
return $this->_data['account_domain'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Username
|
||||
*
|
||||
* @param string $username The username
|
||||
* @return void
|
||||
*/
|
||||
public function set_username( $username )
|
||||
{
|
||||
$this->_data['account_username'] = $username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Username
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_username()
|
||||
{
|
||||
return $this->_data['account_username'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Password
|
||||
*
|
||||
* @param string $password The password
|
||||
* @return void
|
||||
*/
|
||||
public function set_password( $password )
|
||||
{
|
||||
$this->_data['account_password'] = \IPS\Text\Encrypt::fromPlaintext( $password )->tag();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Password
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_password()
|
||||
{
|
||||
return \IPS\Text\Encrypt::fromTag( $this->_data['account_password'] )->decrypt();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set If Account Exists
|
||||
*
|
||||
* @param bool $exists If the account exists
|
||||
* @return void
|
||||
*/
|
||||
public function set_exists( $exists )
|
||||
{
|
||||
$this->_data['account_exists'] = \intval( $exists );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get If Account Exists
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function get_exists()
|
||||
{
|
||||
return (bool) $this->_data['account_exists'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Purchase
|
||||
*
|
||||
* @return \IPS\nexus\Purchase
|
||||
*/
|
||||
public function purchase()
|
||||
{
|
||||
return \IPS\nexus\Purchase::load( $this->_data['ps_id'] );
|
||||
}
|
||||
|
||||
/* !Account */
|
||||
|
||||
/**
|
||||
* Get FTP Link
|
||||
*
|
||||
* @return \IPS\Http\Url
|
||||
*/
|
||||
public function ftpLink()
|
||||
{
|
||||
return \IPS\Http\Url::external( 'ftp://' . $this->username . ':' . urlencode( $this->password ) . '@' . $this->server->hostname );
|
||||
}
|
||||
|
||||
/* !abstract */
|
||||
|
||||
/**
|
||||
* Create
|
||||
*
|
||||
* @param \IPS\nexus\Package\Hosting $package The package
|
||||
* @param \IPS\nexus\Customer $customer The customer
|
||||
* @return void
|
||||
*/
|
||||
public function create( \IPS\nexus\Package\Hosting $package, \IPS\nexus\Customer $customer )
|
||||
{
|
||||
throw new \IPS\nexus\Hosting\Exception( $this, 'DUMMY_SERVER' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get diskspace allowance (in bytes)
|
||||
*
|
||||
* @return int|NULL
|
||||
*/
|
||||
public function diskspaceAllowance()
|
||||
{
|
||||
throw new \IPS\nexus\Hosting\Exception( $this, 'DUMMY_SERVER' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get diskspace currently in use (in bytes)
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function diskspaceInUse()
|
||||
{
|
||||
throw new \IPS\nexus\Hosting\Exception( $this, 'DUMMY_SERVER' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get monthly bandwidth allowance (in bytes)
|
||||
*
|
||||
* @return int|NULL
|
||||
*/
|
||||
public function monthlyBandwidthAllowance()
|
||||
{
|
||||
throw new \IPS\nexus\Hosting\Exception( $this, 'DUMMY_SERVER' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get bandwidth used this month (in bytes)
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function bandwidthUsedThisMonth()
|
||||
{
|
||||
throw new \IPS\nexus\Hosting\Exception( $this, 'DUMMY_SERVER' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get max FTP accounts
|
||||
*
|
||||
* @return int|NULL
|
||||
*/
|
||||
public function maxFtpAccounts()
|
||||
{
|
||||
throw new \IPS\nexus\Hosting\Exception( $this, 'DUMMY_SERVER' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get max databases
|
||||
*
|
||||
* @return int|NULL
|
||||
*/
|
||||
public function maxDatabases()
|
||||
{
|
||||
throw new \IPS\nexus\Hosting\Exception( $this, 'DUMMY_SERVER' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get max email accounts
|
||||
*
|
||||
* @return int|NULL
|
||||
*/
|
||||
public function maxEmailAccounts()
|
||||
{
|
||||
throw new \IPS\nexus\Hosting\Exception( $this, 'DUMMY_SERVER' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get max mailing lists
|
||||
*
|
||||
* @return int|NULL
|
||||
*/
|
||||
public function maxMailingLists()
|
||||
{
|
||||
throw new \IPS\nexus\Hosting\Exception( $this, 'DUMMY_SERVER' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get max subdomains
|
||||
*
|
||||
* @return int|NULL
|
||||
*/
|
||||
public function maxSubdomains()
|
||||
{
|
||||
throw new \IPS\nexus\Hosting\Exception( $this, 'DUMMY_SERVER' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get max parked domains
|
||||
*
|
||||
* @return int|NULL
|
||||
*/
|
||||
public function maxParkedDomains()
|
||||
{
|
||||
throw new \IPS\nexus\Hosting\Exception( $this, 'DUMMY_SERVER' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get max addon domains
|
||||
*
|
||||
* @return int|NULL
|
||||
*/
|
||||
public function maxAddonDomains()
|
||||
{
|
||||
throw new \IPS\nexus\Hosting\Exception( $this, 'DUMMY_SERVER' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Has SSH access?
|
||||
*
|
||||
* @return int|NULL
|
||||
*/
|
||||
public function hasSSHAccess()
|
||||
{
|
||||
throw new \IPS\nexus\Hosting\Exception( $this, 'DUMMY_SERVER' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Control Panel Link
|
||||
*
|
||||
* @return \IPS\Http\Url
|
||||
*/
|
||||
public function controlPanelLink()
|
||||
{
|
||||
throw new \IPS\nexus\Hosting\Exception( $this, 'DUMMY_SERVER' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Change Domain
|
||||
*
|
||||
* @param string $domain New Domain
|
||||
* @param bool $keepOld If true, will park this domain, keeping the old domain as the main domain (rather than just changing the main domain)
|
||||
* @return void
|
||||
*/
|
||||
public function changeDomain( $domain, $park=FALSE )
|
||||
{
|
||||
throw new \IPS\nexus\Hosting\Exception( $this, 'DUMMY_SERVER' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit Priviledges
|
||||
*
|
||||
* @param array $values Values from form
|
||||
* @return void
|
||||
*/
|
||||
public function edit( $values )
|
||||
{
|
||||
throw new \IPS\nexus\Hosting\Exception( $this, 'DUMMY_SERVER' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Change bandwidth limit
|
||||
*
|
||||
* @param float $newLimit New Limit (in bytes)
|
||||
* @param string|NULL $username Username. Only needed if has been changed on server but not locally
|
||||
* @return void
|
||||
*/
|
||||
public function changeBandwidthLimit( $newLimit, $username = NULL )
|
||||
{
|
||||
throw new \IPS\nexus\Hosting\Exception( $this, 'DUMMY_SERVER' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Change Password
|
||||
*
|
||||
* @param string $newPassword New Password
|
||||
* @param string|NULL $username Username. Only needed if has been changed on server but not locally
|
||||
* @return void
|
||||
*/
|
||||
public function changePassword( $newPassword, $username = NULL )
|
||||
{
|
||||
throw new \IPS\nexus\Hosting\Exception( $this, 'DUMMY_SERVER' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Park a domain
|
||||
*
|
||||
* @param string $domain Domain to be parked
|
||||
* @return void
|
||||
*/
|
||||
public function parkDomain( $domain )
|
||||
{
|
||||
throw new \IPS\nexus\Hosting\Exception( $this, 'DUMMY_SERVER' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Suspend
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function suspend()
|
||||
{
|
||||
throw new \IPS\nexus\Hosting\Exception( $this, 'DUMMY_SERVER' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsuspend
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function unsuspend()
|
||||
{
|
||||
throw new \IPS\nexus\Hosting\Exception( $this, 'DUMMY_SERVER' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminate
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function terminate()
|
||||
{
|
||||
throw new \IPS\nexus\Hosting\Exception( $this, 'DUMMY_SERVER' );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,442 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Hosting Account Model
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
* @package Invision Community
|
||||
* @subpackage Nexus
|
||||
* @since 6 Aug 2014
|
||||
*/
|
||||
|
||||
namespace IPS\nexus\Hosting\Cpanel;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hosting Account Model
|
||||
*/
|
||||
class _Account extends \IPS\nexus\Hosting\Account
|
||||
{
|
||||
/**
|
||||
* Create
|
||||
*
|
||||
* @param \IPS\nexus\Package\Hosting $package The package
|
||||
* @param \IPS\nexus\Customer $customer The customer
|
||||
* @return void
|
||||
*/
|
||||
public function create( \IPS\nexus\Package\Hosting $package, \IPS\nexus\Customer $customer )
|
||||
{
|
||||
$response = $this->server->api( 'createacct', array(
|
||||
'username' => $this->username,
|
||||
'domain' => $this->domain,
|
||||
'quota' => ( $package->quota == -1 ) ? 'unlimited' : floor( $package->quota * 1.024 ),
|
||||
'password' => $this->password,
|
||||
'ip' => $package->ip ? 'y' : 'n',
|
||||
'cgi' => $package->cgi,
|
||||
'frontpage' => $package->frontpage,
|
||||
'hasshell' => $package->hasshell,
|
||||
'contactemail' => $customer->email,
|
||||
'maxftp' => ( $package->maxftp == -1 ) ? 'unlimited' : $package->maxftp,
|
||||
'maxsql' => ( $package->maxsql == -1 ) ? 'unlimited' : $package->maxsql,
|
||||
'maxpop' => ( $package->maxpop == -1 ) ? 'unlimited' : $package->maxpop,
|
||||
'maxlst' => ( $package->maxlst == -1 ) ? 'unlimited' : $package->maxlst,
|
||||
'maxsub' => ( $package->maxsub == -1 ) ? 'unlimited' : $package->maxsub,
|
||||
'maxpark' => ( $package->maxpark == -1 ) ? 'unlimited' : $package->maxpark,
|
||||
'maxaddon' => ( $package->maxaddon == -1 ) ? 'unlimited' : $package->maxaddon,
|
||||
'bwlimit' => ( $package->bwlimit == -1 ) ? 'unlimited' : floor( $package->bwlimit * 1.024 ),
|
||||
) );
|
||||
|
||||
if ( !$response['result'][0]['status'] )
|
||||
{
|
||||
throw new \IPS\nexus\Hosting\Exception( $this->server, $response['result'][0]['statusmsg'] );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Account Summary
|
||||
*/
|
||||
protected $summary;
|
||||
|
||||
/**
|
||||
* Get Information
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function summary()
|
||||
{
|
||||
if ( $this->summary === NULL )
|
||||
{
|
||||
$response = $this->server->api( 'accountsummary', array( 'user' => $this->username ), 15 );
|
||||
|
||||
if ( !$response['status'] )
|
||||
{
|
||||
throw new \IPS\nexus\Hosting\Exception( $this->server, $response['statusmsg'] );
|
||||
}
|
||||
|
||||
$this->summary = $response['acct'][0];
|
||||
}
|
||||
|
||||
return $this->summary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get diskspace allowance (in bytes)
|
||||
*
|
||||
* @return int|NULL
|
||||
*/
|
||||
public function diskspaceAllowance()
|
||||
{
|
||||
$information = $this->summary();
|
||||
|
||||
return ( $information['disklimit'] === 'unlimited' ) ? NULL : ( \intval( $information['disklimit'] ) * 1048576 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get diskspace currently in use (in bytes)
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function diskspaceInUse()
|
||||
{
|
||||
$information = $this->summary();
|
||||
|
||||
return \intval( $information['diskused'] ) * 1048576;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Bandwidth used this month
|
||||
*/
|
||||
protected $monthlyBandwidthAllowance;
|
||||
|
||||
/**
|
||||
* @brief Bandwidth used this month
|
||||
*/
|
||||
protected $bandwidthUsedThisMonth;
|
||||
|
||||
/**
|
||||
* Get bandwidth information
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function bandwidth()
|
||||
{
|
||||
$response = $this->server->api( 'showbw' );
|
||||
foreach( $response['bandwidth'][0]['acct'] as $data )
|
||||
{
|
||||
if ( $data['user'] == $this->username )
|
||||
{
|
||||
$this->monthlyBandwidthAllowance = $data['limit'];
|
||||
$this->bandwidthUsedThisMonth = $data['totalbytes'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get monthly bandwidth allowance (in bytes)
|
||||
*
|
||||
* @return int|NULL
|
||||
*/
|
||||
public function monthlyBandwidthAllowance()
|
||||
{
|
||||
if ( $this->monthlyBandwidthAllowance === NULL )
|
||||
{
|
||||
$this->bandwidth();
|
||||
}
|
||||
return ( $this->monthlyBandwidthAllowance === 'unlimited' ) ? NULL : $this->monthlyBandwidthAllowance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get bandwidth used this month (in bytes)
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function bandwidthUsedThisMonth()
|
||||
{
|
||||
if ( $this->bandwidthUsedThisMonth === NULL )
|
||||
{
|
||||
$this->bandwidth();
|
||||
}
|
||||
return $this->bandwidthUsedThisMonth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get max FTP accounts
|
||||
*
|
||||
* @return int|NULL
|
||||
*/
|
||||
public function maxFtpAccounts()
|
||||
{
|
||||
$information = $this->summary();
|
||||
return ( $information['maxftp'] === 'unlimited' ) ? NULL : $information['maxftp'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get max databases
|
||||
*
|
||||
* @return int|NULL
|
||||
*/
|
||||
public function maxDatabases()
|
||||
{
|
||||
$information = $this->summary();
|
||||
return ( $information['maxsql'] === 'unlimited' ) ? NULL : $information['maxsql'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get max email accounts
|
||||
*
|
||||
* @return int|NULL
|
||||
*/
|
||||
public function maxEmailAccounts()
|
||||
{
|
||||
$information = $this->summary();
|
||||
return ( $information['maxpop'] === 'unlimited' ) ? NULL : $information['maxpop'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get max mailing lists
|
||||
*
|
||||
* @return int|NULL
|
||||
*/
|
||||
public function maxMailingLists()
|
||||
{
|
||||
$information = $this->summary();
|
||||
return ( $information['maxlst'] === 'unlimited' ) ? NULL : $information['maxlst'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get max subdomains
|
||||
*
|
||||
* @return int|NULL
|
||||
*/
|
||||
public function maxSubdomains()
|
||||
{
|
||||
$information = $this->summary();
|
||||
return ( $information['maxsub'] === 'unlimited' ) ? NULL : $information['maxsub'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get max parked domains
|
||||
*
|
||||
* @return int|NULL
|
||||
*/
|
||||
public function maxParkedDomains()
|
||||
{
|
||||
$information = $this->summary();
|
||||
return ( $information['maxparked'] === 'unlimited' ) ? NULL : $information['maxparked'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get max addon domains
|
||||
*
|
||||
* @return int|NULL
|
||||
*/
|
||||
public function maxAddonDomains()
|
||||
{
|
||||
$information = $this->summary();
|
||||
return ( $information['maxaddons'] === 'unlimited' ) ? NULL : $information['maxaddons'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Has SSH access?
|
||||
*
|
||||
* @return int|NULL
|
||||
*/
|
||||
public function hasSSHAccess()
|
||||
{
|
||||
$information = $this->summary();
|
||||
return ( mb_substr( $information['shell'], -7 ) != 'noshell' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Control Panel Link
|
||||
*
|
||||
* @return \IPS\Http\Url
|
||||
*/
|
||||
public function controlPanelLink()
|
||||
{
|
||||
return \IPS\Http\Url::external( 'https://' . $this->server->hostname . ':2083' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Change Domain
|
||||
*
|
||||
* @param string $domain New Domain
|
||||
* @param bool $park If true, will park this domain, keeping the old domain as the main domain (rather than just changing the main domain)
|
||||
* @return void
|
||||
*/
|
||||
public function changeDomain( $domain, $park=FALSE )
|
||||
{
|
||||
if ( $park )
|
||||
{
|
||||
return $this->parkDomain( $domain );
|
||||
}
|
||||
else
|
||||
{
|
||||
$response = $this->server->api( 'modifyacct', array( 'user' => $this->username, 'DNS' => $domain ) );
|
||||
if( !$response['result'][0]['status'] )
|
||||
{
|
||||
throw new \IPS\nexus\Hosting\Exception( $this->server, $response['result'][0]['statusmsg'] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit Priviledges
|
||||
*
|
||||
* @param array $values Values from form
|
||||
* @return void
|
||||
*/
|
||||
public function edit( $values )
|
||||
{
|
||||
/* Init */
|
||||
$data = array( 'user' => $this->username );
|
||||
|
||||
/* Set data */
|
||||
foreach ( array( 'DNS' => 'account_domain', 'QUOTA' => 'p_quota', 'MAXFTP' => 'p_maxftp', 'MAXSQL' => 'p_maxsql', 'MAXPOP' => 'p_maxpop', 'MAXLST' => 'p_maxlst', 'MAXSUB' => 'p_maxsub', 'MAXPARK' => 'p_maxpark', 'MAXADDON' => 'p_maxaddon', 'HASSHELL' => 'p_hasshell' ) as $cpanelKey => $formKey )
|
||||
{
|
||||
if ( isset( $values[ $formKey ] ) )
|
||||
{
|
||||
if ( $values[ $formKey ] == -1 )
|
||||
{
|
||||
$values[ $formKey ] = 'unlimited';
|
||||
}
|
||||
elseif ( $cpanelKey === 'QUOTA' )
|
||||
{
|
||||
$values[ $formKey ] = $values[ $formKey ] * 1048576 * 1.024;
|
||||
}
|
||||
|
||||
$data[ $cpanelKey ] = $values[ $formKey ];
|
||||
}
|
||||
}
|
||||
|
||||
/* Has the username changed? */
|
||||
if ( isset( $values['account_username'] ) AND $values['account_username'] != $this->username )
|
||||
{
|
||||
$data['newuser'] = $values['account_username'];
|
||||
}
|
||||
|
||||
/* Send */
|
||||
$response = $this->server->api( 'modifyacct', $data );
|
||||
if( !$response['result'][0]['status'] )
|
||||
{
|
||||
throw new \IPS\nexus\Hosting\Exception( $this->server, $response['result'][0]['statusmsg'] );
|
||||
}
|
||||
|
||||
/* Change bandwidth limit? */
|
||||
if ( isset( $values['p_bwlimit'] ) )
|
||||
{
|
||||
if ( ( $values['p_bwlimit'] == -1 and $this->monthlyBandwidthAllowance() !== NULL ) or ( $values['p_bwlimit'] != -1 and $values['p_bwlimit'] != ( $this->monthlyBandwidthAllowance() / 1048576 / 1.024 ) ) )
|
||||
{
|
||||
$this->changeBandwidthLimit( $values['p_bwlimit'] * 1048576 / 1.024, $values['account_username'] );
|
||||
}
|
||||
}
|
||||
|
||||
/* Change password? */
|
||||
if ( isset( $values['account_password'] ) and $values['account_password'] != (string) $this->password )
|
||||
{
|
||||
$this->changePassword( $values['account_password'], $this->username );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change bandwidth limit
|
||||
*
|
||||
* @param float $newLimit New Limit (in bytes)
|
||||
* @param string|NULL $username Username. Only needed if has been changed on server but not locally
|
||||
* @return void
|
||||
*/
|
||||
public function changeBandwidthLimit( $newLimit, $username = NULL )
|
||||
{
|
||||
$response = $this->server->api( 'limitbw', array( 'user' => $username ?: $this->username, 'bwlimit' => floor( $newLimit / 1000000 ) ) );
|
||||
if( !$response['result'][0]['status'] )
|
||||
{
|
||||
throw new \IPS\nexus\Hosting\Exception( $this->server, $response['result'][0]['statusmsg'] );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change Password
|
||||
*
|
||||
* @param string $newPassword New Password
|
||||
* @param string|NULL $username Username. Only needed if has been changed on server but not locally
|
||||
* @return void
|
||||
*/
|
||||
public function changePassword( $newPassword, $username = NULL )
|
||||
{
|
||||
$response = $this->server->api( 'passwd', array( 'user' => $username ?: $this->username, 'pass' => $newPassword ) );
|
||||
if( !$response['passwd'][0]['status'] )
|
||||
{
|
||||
throw new \IPS\nexus\Hosting\Exception( $this->server, $response['passwd'][0]['statusmsg'] );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Park a domain
|
||||
*
|
||||
* @param string $domain Domain to be parked
|
||||
* @return void
|
||||
*/
|
||||
public function parkDomain( $domain )
|
||||
{
|
||||
$response = $this->server->api( 'cpanel', array(
|
||||
'cpanel_jsonapi_user' => $this->username,
|
||||
'cpanel_jsonapi_apiversion' => 2,
|
||||
'cpanel_jsonapi_module' => 'Park',
|
||||
'cpanel_jsonapi_func' => 'park',
|
||||
'domain' => $domain
|
||||
) );
|
||||
if( !$response['cpanelresult']['data'][0]['result'] )
|
||||
{
|
||||
throw new \IPS\nexus\Hosting\Exception( $this->server, $response['cpanelresult']['data'][0]['reason'] );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Suspend
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function suspend()
|
||||
{
|
||||
$response = $this->server->api( 'suspendacct', array( 'user' => $this->username ) );
|
||||
if( !$response['result'][0]['status'] )
|
||||
{
|
||||
throw new \IPS\nexus\Hosting\Exception( $this->server, $response['result'][0]['statusmsg'] );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsuspend
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function unsuspend()
|
||||
{
|
||||
$response = $this->server->api( 'unsuspendacct', array( 'user' => $this->username ) );
|
||||
if( !$response['result'][0]['status'] )
|
||||
{
|
||||
throw new \IPS\nexus\Hosting\Exception( $this->server, $response['result'][0]['statusmsg'] );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminate
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function terminate()
|
||||
{
|
||||
$response = $this->server->api( 'removeacct', array( 'user' => $this->username ) );
|
||||
if( !$response['result'][0]['status'] )
|
||||
{
|
||||
throw new \IPS\nexus\Hosting\Exception( $this->server, $response['result'][0]['statusmsg'] );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,222 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief CPanel/WHM Server Model
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
* @package Invision Community
|
||||
* @subpackage Nexus
|
||||
* @since 6 Aug 2014
|
||||
*/
|
||||
|
||||
namespace IPS\nexus\Hosting\Cpanel;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* CPanel/WHM Server Model
|
||||
*/
|
||||
class _Server extends \IPS\nexus\Hosting\Server
|
||||
{
|
||||
/**
|
||||
* Additional fields for the server form
|
||||
*
|
||||
* @param \IPS\nexus\Hosting\Server $server The server bring edited
|
||||
* @return array
|
||||
*/
|
||||
public static function extraFormFields( \IPS\nexus\Hosting\Server $server )
|
||||
{
|
||||
$class = \get_class();
|
||||
return array(
|
||||
'server_cpanel_username' => new \IPS\Helpers\Form\Text( 'server_cpanel_username', $server->username ?: 'root', NULL, array(), function( $val )
|
||||
{
|
||||
if ( !$val and \IPS\Request::i()->server_type == 'cpanel' )
|
||||
{
|
||||
throw new \DomainException('form_required');
|
||||
}
|
||||
}, NULL, NULL, 'server_cpanel_username' ),
|
||||
'server_cpanel_key' => new \IPS\Helpers\Form\TextArea( 'server_cpanel_key', $server->access, NULL, array( 'rows' => 20 ), function( $val ) use ( $class )
|
||||
{
|
||||
if ( \IPS\Request::i()->server_type == 'cpanel' )
|
||||
{
|
||||
if ( !$val )
|
||||
{
|
||||
throw new \DomainException('form_required');
|
||||
}
|
||||
else
|
||||
{
|
||||
$server = new $class;
|
||||
$server->hostname = \IPS\Request::i()->server_hostname;
|
||||
$server->ip = \IPS\Request::i()->server_ip;
|
||||
$server->username = \IPS\Request::i()->server_cpanel_username;
|
||||
$server->access = $val;
|
||||
$server->extra = array( 'call' => \IPS\Request::i()->server_cpanel_call );
|
||||
|
||||
try
|
||||
{
|
||||
$response = $server->api( 'gethostname' );
|
||||
}
|
||||
catch ( \IPS\nexus\Hosting\Exception $e )
|
||||
{
|
||||
throw new \DomainException( $e->getMessage() );
|
||||
}
|
||||
|
||||
if ( $response['hostname'] !== $server->hostname )
|
||||
{
|
||||
throw new \DomainException( \IPS\Member::loggedIn()->language()->addToStack( 'server_cpanel_key_hostname_err', FALSE, array( 'sprintf' => array( $response['hostname'] ) ) ) );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}, NULL, NULL, 'server_cpanel_key' ),
|
||||
'server_cpanel_call' => new \IPS\Helpers\Form\Radio( 'server_cpanel_call', isset( $server->extra['call'] ) ? $server->extra['call'] : 'hostname', NULL, array( 'options' => array( 'hostname' => 'server_hostname', 'ip' => 'server_ip' ) ), NULL, NULL, NULL, 'server_cpanel_call' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* [Node] Format form values from add/edit form for save
|
||||
*
|
||||
* @param array $values Values from the form
|
||||
* @return array
|
||||
*/
|
||||
public static function _formatFormValues( $values )
|
||||
{
|
||||
if( isset( $values['server_cpanel_username'] ) )
|
||||
{
|
||||
$values['server_username'] = $values['server_cpanel_username'];
|
||||
unset( $values['server_cpanel_username'] );
|
||||
}
|
||||
|
||||
if( isset( $values['server_cpanel_key'] ) )
|
||||
{
|
||||
$values['server_access'] = trim( $values['server_cpanel_key'] );
|
||||
unset( $values['server_cpanel_key'] );
|
||||
}
|
||||
|
||||
if( isset( $values['server_cpanel_call'] ) )
|
||||
{
|
||||
$values['extra'] = array( 'call' => $values['server_cpanel_call'] );
|
||||
unset( $values['server_cpanel_call'] );
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if username is acceptable
|
||||
*
|
||||
* @param string $username The username to check
|
||||
* @return bool
|
||||
*/
|
||||
public function checkUsername( $username )
|
||||
{
|
||||
if (
|
||||
\in_array( $username, array( 'root', 'virtfs', 'roundcube', 'horde', 'spamassassin', 'eximstats', 'cphulkd', 'modsec', 'all', 'dovecot', 'tomcat', 'postgres', 'mailman', 'proftpd', 'cpbackup', 'files', 'dirs', 'tmp', 'toor', 'munin' ) )
|
||||
OR mb_substr( $username, 0, 4 ) == 'test'
|
||||
OR mb_substr( $username, -7 ) == 'assword'
|
||||
) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* List Accounts
|
||||
*
|
||||
* @return array 'username' => array( 'domain' => 'example.com', 'disklimit' => 100000, 'diskused' => 50000, 'active' => TRUE )
|
||||
*/
|
||||
public function listAccounts()
|
||||
{
|
||||
$return = array();
|
||||
|
||||
$response = $this->api( 'listaccts' );
|
||||
if( !$response['status'] )
|
||||
{
|
||||
throw new \IPS\nexus\Hosting\Exception( $this->server, $response['statusmsg'] );
|
||||
}
|
||||
|
||||
foreach ( $response['acct'] as $account )
|
||||
{
|
||||
$return[ $account['user'] ] = array(
|
||||
'domain' => $account['domain'],
|
||||
'disklimit' => \intval( str_replace( 'M', '', $account['disklimit'] ) ) * 1000000,
|
||||
'diskused' => \intval( str_replace( 'M', '', $account['diskused'] ) ) * 1000000,
|
||||
'active' => !$account['suspended'],
|
||||
);
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reboot Server
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function reboot()
|
||||
{
|
||||
$this->api( 'reboot' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retry Error
|
||||
*
|
||||
* @param array $details Details
|
||||
* @return void
|
||||
*/
|
||||
public function retryError( $details )
|
||||
{
|
||||
$response = $this->api( $details['function'], $details['params'] );
|
||||
|
||||
if ( isset( $response['result'][0] ) and !$response['result'][0]['status'] )
|
||||
{
|
||||
throw new \IPS\nexus\Hosting\Exception( $this, $response['result'][0]['statusmsg'] );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* API Call
|
||||
*
|
||||
* @param string $function Function Name
|
||||
* @param array $params Parameters to send
|
||||
* @param int $timeout Timeout limit
|
||||
* @return void
|
||||
*/
|
||||
public function api( $function, $params=array(), $timeout=90 )
|
||||
{
|
||||
$this->lastCallData = array( 'function' => $function, 'params' => $params );
|
||||
|
||||
$apiCall = isset( $this->extra['call'] ) ? str_replace( 'server_', '', $this->extra['call'] ) : 'ip';
|
||||
|
||||
try
|
||||
{
|
||||
$response = \IPS\Http\Url::external( 'https://' . $this->$apiCall . ':2087/json-api/' . $function )
|
||||
->setQueryString( $params )
|
||||
->request( $timeout )
|
||||
->setHeaders( array( 'Authorization' => "WHM {$this->username}:" . preg_replace( "'(\r|\n)'", '', $this->access ) ) )
|
||||
->get()
|
||||
->decodeJson();
|
||||
|
||||
if ( isset( $response['error'] ) )
|
||||
{
|
||||
throw new \IPS\nexus\Hosting\Exception( $this, $response['error'] );
|
||||
}
|
||||
elseif ( isset( $response['cpanelresult']['error'] ) )
|
||||
{
|
||||
throw new \IPS\nexus\Hosting\Exception( $this, $response['cpanelresult']['error'] );
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
catch ( \IPS\Http\Request\Exception $e )
|
||||
{
|
||||
throw new \IPS\nexus\Hosting\Exception( $this, $e->getMessage() );
|
||||
}
|
||||
}
|
||||
}
|
||||
Whitespace-only changes.
@@ -1,119 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Expected Output Monitoring Rule
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
* @package Invision Community
|
||||
* @subpackage Nexus
|
||||
* @since 12 Sep 2014
|
||||
*/
|
||||
|
||||
namespace IPS\nexus\Hosting;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expected Output Monitoring Rule
|
||||
*/
|
||||
class _EOM extends \IPS\Node\Model
|
||||
{
|
||||
/**
|
||||
* @brief [ActiveRecord] Multiton Store
|
||||
*/
|
||||
protected static $multitons;
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database Table
|
||||
*/
|
||||
public static $databaseTable = 'nexus_eom';
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database Prefix
|
||||
*/
|
||||
public static $databasePrefix = 'eom_';
|
||||
|
||||
/**
|
||||
* @brief [Node] Node Title
|
||||
*/
|
||||
public static $nodeTitle = 'expected_output_monitoring';
|
||||
|
||||
/**
|
||||
* @brief [Node] ACP Restrictions
|
||||
* @code
|
||||
array(
|
||||
'app' => 'core', // The application key which holds the restrictrions
|
||||
'module' => 'foo', // The module key which holds the restrictions
|
||||
'map' => array( // [Optional] The key for each restriction - can alternatively use "prefix"
|
||||
'add' => 'foo_add',
|
||||
'edit' => 'foo_edit',
|
||||
'permissions' => 'foo_perms',
|
||||
'delete' => 'foo_delete'
|
||||
),
|
||||
'all' => 'foo_manage', // [Optional] The key to use for any restriction not provided in the map (only needed if not providing all 4)
|
||||
'prefix' => 'foo_', // [Optional] Rather than specifying each key in the map, you can specify a prefix, and it will automatically look for restrictions with the key "[prefix]_add/edit/permissions/delete"
|
||||
* @endcode
|
||||
*/
|
||||
protected static $restrictions = array(
|
||||
'app' => 'nexus',
|
||||
'module' => 'hosting',
|
||||
'prefix' => 'monitoring_eom_'
|
||||
);
|
||||
|
||||
/**
|
||||
* Get title
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get__title()
|
||||
{
|
||||
return (string) $this->url;
|
||||
}
|
||||
|
||||
/**
|
||||
* [Node] Add/Edit Form
|
||||
*
|
||||
* @param \IPS\Helpers\Form $form The form
|
||||
* @return void
|
||||
*/
|
||||
public function form( &$form )
|
||||
{
|
||||
$form->add( new \IPS\Helpers\Form\Url( 'eom_url', $this->url, TRUE ) );
|
||||
$form->add( new \IPS\Helpers\Form\Radio( 'eom_type', $this->type, TRUE, array( 'options' => array( 'c' => 'eom_type_c', 'e' => 'eom_type_e', 'n' => 'eom_type_n' ) ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\TextArea( 'eom_value', $this->value, TRUE ) );
|
||||
$form->add( new \IPS\Helpers\Form\Stack( 'eom_notify', json_decode( $this->notify, TRUE ), TRUE ) );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* [Node] Format form values from add/edit form for save
|
||||
*
|
||||
* @param array $values Values from the form
|
||||
* @return array
|
||||
*/
|
||||
public function formatFormValues( $values )
|
||||
{
|
||||
if( isset( $values['eom_notify'] ) )
|
||||
{
|
||||
$values['eom_notify'] = json_encode( $values['eom_notify'] );
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* [Node] Perform actions after saving the form
|
||||
*
|
||||
* @param array $values Values from the form
|
||||
* @return void
|
||||
*/
|
||||
public function postSaveForm( $values )
|
||||
{
|
||||
\IPS\Db::i()->update( 'core_tasks', array( 'enabled' => 1 ), "`key`='expectedOutputMonitoring'" );
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Hosting Exception
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
* @package Invision Community
|
||||
* @subpackage Nexus
|
||||
* @since 11 Aug 2014
|
||||
*/
|
||||
|
||||
namespace IPS\nexus\Hosting;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hosting Exception
|
||||
*/
|
||||
class _Exception extends \RuntimeException
|
||||
{
|
||||
/**
|
||||
* @brief Time
|
||||
*/
|
||||
protected $time;
|
||||
|
||||
/**
|
||||
* @brief Server
|
||||
*/
|
||||
protected $server;
|
||||
|
||||
/**
|
||||
* @brief Details
|
||||
*/
|
||||
protected $details;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param \IPS\nexus\Hosting\Server $server Server
|
||||
* @param string $message Message
|
||||
* @return void
|
||||
*/
|
||||
public function __construct( \IPS\nexus\Hosting\Server $server, $message )
|
||||
{
|
||||
$this->time = new \IPS\DateTime;
|
||||
$this->server = $server;
|
||||
$this->details = $server->lastCallData;
|
||||
|
||||
return parent::__construct( $message );
|
||||
}
|
||||
|
||||
/**
|
||||
* Log
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function log()
|
||||
{
|
||||
\IPS\Db::i()->insert( 'nexus_hosting_errors', array(
|
||||
'e_time' => $this->time->getTimestamp(),
|
||||
'e_server' => $this->server->id,
|
||||
'e_message' => $this->getMessage(),
|
||||
'e_extra' => json_encode( $this->details )
|
||||
) );
|
||||
|
||||
\IPS\core\AdminNotification::send( 'nexus', 'HostingError', NULL, TRUE, array( 'server' => $this->server, 'message' => $this->getMessage() ) );
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Dummy Server Model
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
* @package Invision Community
|
||||
* @subpackage Nexus
|
||||
* @since 6 Aug 2014
|
||||
*/
|
||||
|
||||
namespace IPS\nexus\Hosting\None;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dummy Server Model
|
||||
*/
|
||||
class _Server extends \IPS\nexus\Hosting\Server
|
||||
{
|
||||
}
|
||||
Whitespace-only changes.
@@ -1,151 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Hosting Queue Model
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
* @package Invision Community
|
||||
* @subpackage Nexus
|
||||
* @since 6 Aug 2014
|
||||
*/
|
||||
|
||||
namespace IPS\nexus\Hosting;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hosting Queue Model
|
||||
*/
|
||||
class _Queue extends \IPS\Node\Model
|
||||
{
|
||||
/**
|
||||
* @brief [ActiveRecord] Multiton Store
|
||||
*/
|
||||
protected static $multitons;
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database Table
|
||||
*/
|
||||
public static $databaseTable = 'nexus_hosting_queues';
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database Prefix
|
||||
*/
|
||||
public static $databasePrefix = 'queue_';
|
||||
|
||||
/**
|
||||
* @brief [Node] Order Database Column
|
||||
*/
|
||||
public static $databaseColumnOrder = 'name';
|
||||
|
||||
/**
|
||||
* @brief [Node] Automatically set position for new nodes
|
||||
*/
|
||||
public static $automaticPositionDetermination = FALSE;
|
||||
|
||||
/**
|
||||
* @brief [Node] Sortable?
|
||||
*/
|
||||
public static $nodeSortable = FALSE;
|
||||
|
||||
/**
|
||||
* @brief [Node] Node Title
|
||||
*/
|
||||
public static $nodeTitle = 'menu__nexus_hosting_queues';
|
||||
|
||||
/**
|
||||
* @brief [Node] ACP Restrictions
|
||||
* @code
|
||||
array(
|
||||
'app' => 'core', // The application key which holds the restrictrions
|
||||
'module' => 'foo', // The module key which holds the restrictions
|
||||
'map' => array( // [Optional] The key for each restriction - can alternatively use "prefix"
|
||||
'add' => 'foo_add',
|
||||
'edit' => 'foo_edit',
|
||||
'permissions' => 'foo_perms',
|
||||
'delete' => 'foo_delete'
|
||||
),
|
||||
'all' => 'foo_manage', // [Optional] The key to use for any restriction not provided in the map (only needed if not providing all 4)
|
||||
'prefix' => 'foo_', // [Optional] Rather than specifying each key in the map, you can specify a prefix, and it will automatically look for restrictions with the key "[prefix]_add/edit/permissions/delete"
|
||||
* @endcode
|
||||
*/
|
||||
protected static $restrictions = array(
|
||||
'app' => 'nexus',
|
||||
'module' => 'hosting',
|
||||
'prefix' => 'queues_'
|
||||
);
|
||||
|
||||
/**
|
||||
* Get all queues
|
||||
*
|
||||
* @return \IPS\Patterns\ActiveRecordIterator
|
||||
*/
|
||||
public static function queues()
|
||||
{
|
||||
return new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'nexus_hosting_queues', NULL, 'queue_name' ), 'IPS\nexus\Hosting\Queue' );
|
||||
}
|
||||
|
||||
/**
|
||||
* [Node] Add/Edit Form
|
||||
*
|
||||
* @param \IPS\Helpers\Form $form The form
|
||||
* @return void
|
||||
*/
|
||||
public function form( &$form )
|
||||
{
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'queue_name', $this->name, TRUE ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Title
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get__title()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Active Server
|
||||
*
|
||||
* @return \IPS\nexus\Hosting\Server
|
||||
* @throws \UnderflowException
|
||||
*/
|
||||
public function activeServer()
|
||||
{
|
||||
$where = array();
|
||||
$where[] = array( \IPS\Db::i()->findInSet( 'server_queues', array( $this->id ) ) );
|
||||
if ( !\IPS\IN_DEV )
|
||||
{
|
||||
$where[] = array( 'server_type<>?', 'none' );
|
||||
}
|
||||
|
||||
$serverLimits = iterator_to_array( \IPS\Db::i()->select( array( 'server_id', 'server_max_accounts' ), 'nexus_hosting_servers', $where )->setKeyField('server_id')->setValueField('server_max_accounts') );
|
||||
$serverWithLeastAccounts = NULL;
|
||||
foreach ( \IPS\Db::i()->select( 'nexus_hosting_servers.server_id, count(nexus_hosting_accounts.ps_id) AS accounts', 'nexus_hosting_servers', $where, 'accounts ASC', NULL, 'server_id' )->join( 'nexus_hosting_accounts', 'account_server=server_id AND account_exists=1' ) as $serverData )
|
||||
{
|
||||
if ( !$serverWithLeastAccounts )
|
||||
{
|
||||
$serverWithLeastAccounts = $serverData['server_id'];
|
||||
}
|
||||
|
||||
if ( !isset( $serverLimits[ $serverData['server_id'] ] ) or !$serverLimits[ $serverData['server_id'] ] or $serverLimits[ $serverData['server_id'] ] > $serverData['accounts'] )
|
||||
{
|
||||
return \IPS\nexus\Hosting\Server::load( $serverData['server_id'] );
|
||||
}
|
||||
}
|
||||
|
||||
if ( $serverWithLeastAccounts )
|
||||
{
|
||||
return \IPS\nexus\Hosting\Server::load( $serverWithLeastAccounts );
|
||||
}
|
||||
|
||||
throw new \UnderflowException;
|
||||
}
|
||||
}
|
||||
@@ -1,414 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Hosting Server Model
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
* @package Invision Community
|
||||
* @subpackage Nexus
|
||||
* @since 6 Aug 2014
|
||||
*/
|
||||
|
||||
namespace IPS\nexus\Hosting;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hosting Server Model
|
||||
*/
|
||||
class _Server extends \IPS\Node\Model
|
||||
{
|
||||
/**
|
||||
* Server Types
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function serverTypes()
|
||||
{
|
||||
return array(
|
||||
'Cpanel' => 'IPS\nexus\Hosting\Cpanel\Server',
|
||||
'None' => 'IPS\nexus\Hosting\None\Server',
|
||||
);
|
||||
}
|
||||
|
||||
/* !ActiveRecord */
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Multiton Store
|
||||
*/
|
||||
protected static $multitons;
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database Table
|
||||
*/
|
||||
public static $databaseTable = 'nexus_hosting_servers';
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database Prefix
|
||||
*/
|
||||
public static $databasePrefix = 'server_';
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database ID Fields
|
||||
*/
|
||||
protected static $databaseIdFields = array( 'server_hostname', 'server_dedicated' );
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Multiton Map
|
||||
*/
|
||||
protected static $multitonMap = array();
|
||||
|
||||
/**
|
||||
* Construct ActiveRecord from database row
|
||||
*
|
||||
* @param array $data Row from database table
|
||||
* @param bool $updateMultitonStoreIfExists Replace current object in multiton store if it already exists there?
|
||||
* @return static
|
||||
*/
|
||||
public static function constructFromData( $data, $updateMultitonStoreIfExists = TRUE )
|
||||
{
|
||||
/* Initiate an object */
|
||||
$availableTypes = static::serverTypes();
|
||||
$classname = $availableTypes[ mb_ucfirst( $data['server_type'] ) ];
|
||||
$obj = new $classname;
|
||||
$obj->_new = FALSE;
|
||||
|
||||
/* Import data */
|
||||
foreach ( $data as $k => $v )
|
||||
{
|
||||
if( static::$databasePrefix )
|
||||
{
|
||||
$k = \substr( $k, \strlen( static::$databasePrefix ) );
|
||||
}
|
||||
|
||||
$obj->_data[ $k ] = $v;
|
||||
}
|
||||
$obj->changed = array();
|
||||
|
||||
/* Return */
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Title
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get__title()
|
||||
{
|
||||
return $this->hostname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get extra information
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_extra()
|
||||
{
|
||||
return isset( $this->_data['extra'] ) ? json_decode( $this->_data['extra'], TRUE ) : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set extra information
|
||||
*
|
||||
* @param mixed $extra The data
|
||||
* @return void
|
||||
*/
|
||||
public function set_extra( $extra )
|
||||
{
|
||||
$this->_data['extra'] = json_encode( $extra );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the server type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_type()
|
||||
{
|
||||
return ( isset( $this->_data['type'] ) ) ? mb_ucfirst( $this->_data['type'] ) : '';
|
||||
}
|
||||
|
||||
/* !Node */
|
||||
|
||||
/**
|
||||
* @brief [Node] Node Title
|
||||
*/
|
||||
public static $nodeTitle = 'menu__nexus_hosting_servers';
|
||||
|
||||
/**
|
||||
* @brief [Node] ACP Restrictions
|
||||
* @code
|
||||
array(
|
||||
'app' => 'core', // The application key which holds the restrictrions
|
||||
'module' => 'foo', // The module key which holds the restrictions
|
||||
'map' => array( // [Optional] The key for each restriction - can alternatively use "prefix"
|
||||
'add' => 'foo_add',
|
||||
'edit' => 'foo_edit',
|
||||
'permissions' => 'foo_perms',
|
||||
'delete' => 'foo_delete'
|
||||
),
|
||||
'all' => 'foo_manage', // [Optional] The key to use for any restriction not provided in the map (only needed if not providing all 4)
|
||||
'prefix' => 'foo_', // [Optional] Rather than specifying each key in the map, you can specify a prefix, and it will automatically look for restrictions with the key "[prefix]_add/edit/permissions/delete"
|
||||
* @endcode
|
||||
*/
|
||||
protected static $restrictions = array(
|
||||
'app' => 'nexus',
|
||||
'module' => 'hosting',
|
||||
'prefix' => 'servers_'
|
||||
);
|
||||
|
||||
/**
|
||||
* [Node] Add/Edit Form
|
||||
*
|
||||
* @param \IPS\Helpers\Form $form The form
|
||||
* @return void
|
||||
*/
|
||||
public function form( &$form )
|
||||
{
|
||||
$types = array();
|
||||
$extraFields = array();
|
||||
$toggles = array();
|
||||
foreach ( static::serverTypes() as $key => $class )
|
||||
{
|
||||
$types[ $key ] = 'nexus_server_' . $key;
|
||||
|
||||
$extraFields[ $key ] = $class::extraFormFields( $this );
|
||||
$toggles[ $key ] = array_keys( $extraFields[ $key ] );
|
||||
}
|
||||
|
||||
$form->addHeader( 'server_type' );
|
||||
$form->add( new \IPS\Helpers\Form\Radio( 'server_type', $this->type, TRUE, array( 'options' => $types, 'toggles' => $toggles ) ) );
|
||||
foreach ( $extraFields as $type => $fields )
|
||||
{
|
||||
foreach ( $fields as $id => $field )
|
||||
{
|
||||
$form->add( $field );
|
||||
}
|
||||
}
|
||||
$form->addHeader( 'server_network_information' );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'server_hostname', $this->hostname, TRUE, array( 'placeholder' => 'server.example.net' ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'server_ip', $this->ip, TRUE, array( 'placeholder' => '123.456.789.000' ), function( $val )
|
||||
{
|
||||
if ( !filter_var( $val, FILTER_VALIDATE_IP ) )
|
||||
{
|
||||
throw new \DomainException('form_bad_value');
|
||||
}
|
||||
} ) );
|
||||
$form->addHeader( 'server_accounts' );
|
||||
$form->add( new \IPS\Helpers\Form\Node( 'server_queues', explode( ',', $this->queues ), FALSE, array( 'class' => 'IPS\nexus\Hosting\Queue', 'multiple' => TRUE ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Number( 'server_max_accounts', $this->id ? $this->max_accounts : 0, FALSE, array( 'unlimited' => 0 ) ) );
|
||||
$nameservers = $this->nameservers ? explode( ',', $this->nameservers ) : array();
|
||||
$form->add( new \IPS\Helpers\Form\Radio( 'server_nameservers_default', \count( $nameservers ) ? 0 : 1, FALSE, array( 'options' => array( 1 => 'server_nameservers_default_yes', 0 => 'server_nameservers_default_no' ), 'toggles' => array( 0 => array( 'server_nameservers' ) ) ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Stack( 'server_nameservers', $nameservers, NULL, array( 'placeholder' => 'ns.example.net' ), NULL, NULL, NULL, 'server_nameservers' ) );
|
||||
$form->addHeader( 'server_reporting' );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'server_monitor_on', \IPS\Settings::i()->monitoring_script and $this->monitor, FALSE, array( 'disabled' => !\IPS\Settings::i()->monitoring_script, 'togglesOn' => array( 'server_monitor' ) ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Url( 'server_monitor', $this->monitor ?: '', NULL, array( 'placeholder' => 'http://server.example.net/monitor_remote.php' ), function( $val )
|
||||
{
|
||||
if ( !$val and \IPS\Request::i()->sever_monitor_on )
|
||||
{
|
||||
throw new \DomainException('form_required');
|
||||
}
|
||||
}, NULL, NULL, 'server_monitor' ) );
|
||||
$currencies = \IPS\nexus\Money::currencies();
|
||||
$form->add( new \IPS\Helpers\Form\Number( 'server_cost', $this->cost, FALSE, array(), NULL, NULL, array_shift( $currencies ) ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* [Node] Format form values from add/edit form for save
|
||||
*
|
||||
* @param array $values Values from the form
|
||||
* @return array
|
||||
*/
|
||||
public function formatFormValues( $values )
|
||||
{
|
||||
if( isset( $values['server_queues'] ) )
|
||||
{
|
||||
$values['server_queues'] = \is_array( $values['server_queues'] ) ? implode( ',', array_keys( $values['server_queues'] ) ) : NULL;
|
||||
}
|
||||
|
||||
if( isset( $values['server_nameservers'] ) )
|
||||
{
|
||||
$values['server_nameservers'] = implode( ',', $values['server_nameservers'] );
|
||||
}
|
||||
|
||||
unset( $values['server_nameservers_default'] );
|
||||
unset( $values['server_monitor_on'] );
|
||||
|
||||
$availableTypes = static::serverTypes();
|
||||
$serverClass = $availableTypes[ mb_ucfirst( $values['server_type'] ) ];
|
||||
$values = $serverClass::_formatFormValues( $values );
|
||||
|
||||
foreach ( static::serverTypes() as $key => $class )
|
||||
{
|
||||
foreach ( array_keys( $class::extraFormFields( $this ) ) as $k )
|
||||
{
|
||||
unset( $values[ $k ] );
|
||||
}
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* [Node] Format form values from add/edit form for save
|
||||
*
|
||||
* @param array $values Values from the form
|
||||
* @return array
|
||||
*/
|
||||
public static function _formatFormValues( $values )
|
||||
{
|
||||
return $values;
|
||||
}
|
||||
|
||||
/* !Server */
|
||||
|
||||
/**
|
||||
* Last API call data
|
||||
*/
|
||||
public $lastCallData;
|
||||
|
||||
/**
|
||||
* Get nameservers
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function nameservers()
|
||||
{
|
||||
return explode( ',', $this->nameservers ?: \IPS\Settings::i()->nexus_hosting_nameservers );
|
||||
}
|
||||
|
||||
/**
|
||||
* Monitoring: Server Online
|
||||
*
|
||||
* @param mixed $version Version number returned from script
|
||||
* @return void
|
||||
*/
|
||||
public function monitoringOnline( $version )
|
||||
{
|
||||
foreach ( explode( ',', \IPS\Settings::i()->monitoring_alert ) as $address )
|
||||
{
|
||||
$email = \IPS\Email::buildFromTemplate( 'nexus', 'monitoring_online', array( $this, urlencode( $address ) ), \IPS\Email::TYPE_LIST );
|
||||
$email->send( $address );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Monitoring: Server Offline
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function monitoringOffline()
|
||||
{
|
||||
foreach ( explode( ',', \IPS\Settings::i()->monitoring_alert ) as $address )
|
||||
{
|
||||
$email = \IPS\Email::buildFromTemplate( 'nexus', 'monitoring_offline', array( $this, urlencode( $address ) ), \IPS\Email::TYPE_LIST );
|
||||
$email->send( $address );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Monitoring: Server Offline - Second Notification
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function monitoringPanic()
|
||||
{
|
||||
foreach ( explode( ',', \IPS\Settings::i()->monitoring_alert ) as $address )
|
||||
{
|
||||
$email = \IPS\Email::buildFromTemplate( 'nexus', 'monitoring_panic', array( $this, urlencode( $address ) ), \IPS\Email::TYPE_LIST );
|
||||
$email->send( $address );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Monitoring: Server Reset
|
||||
*
|
||||
* @param string $by Email address
|
||||
* @return void
|
||||
*/
|
||||
public function monitoringAcknowledged( $by )
|
||||
{
|
||||
foreach ( explode( ',', \IPS\Settings::i()->monitoring_alert ) as $address )
|
||||
{
|
||||
$email = \IPS\Email::buildFromTemplate( 'nexus', 'monitoring_acknowledged', array( $this, urlencode( $address ), $by ), \IPS\Email::TYPE_LIST );
|
||||
$email->send( $address );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Monitoring: Server Reset
|
||||
*
|
||||
* @param string $by Email address
|
||||
* @return void
|
||||
*/
|
||||
public function monitoringReset( $by )
|
||||
{
|
||||
foreach ( explode( ',', \IPS\Settings::i()->monitoring_alert ) as $address )
|
||||
{
|
||||
$email = \IPS\Email::buildFromTemplate( 'nexus', 'monitoring_reset', array( $this, urlencode( $address ), $by ), \IPS\Email::TYPE_LIST );
|
||||
$email->send( $address );
|
||||
}
|
||||
}
|
||||
|
||||
/* !abstract */
|
||||
|
||||
/**
|
||||
* Additional fields for the server form
|
||||
*
|
||||
* @param \IPS\nexus\Hosting\Server $server The server bring edited
|
||||
* @return array
|
||||
*/
|
||||
public static function extraFormFields( \IPS\nexus\Hosting\Server $server )
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if username is acceptable
|
||||
*
|
||||
* @param string $username The username to check
|
||||
* @return bool
|
||||
*/
|
||||
public function checkUsername( $username )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* List Accounts
|
||||
*
|
||||
* @return array 'username' => array( 'domain' => 'example.com', 'disklimit' => 100000, 'diskused' => 50000, 'active' => TRUE )
|
||||
*/
|
||||
public function listAccounts()
|
||||
{
|
||||
throw new \IPS\nexus\Hosting\Exception( $this, 'DUMMY_SERVER' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Reboot Server
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function reboot()
|
||||
{
|
||||
throw new \IPS\nexus\Hosting\Exception( $this, 'DUMMY_SERVER' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retry Error
|
||||
*
|
||||
* @param array $details Details
|
||||
* @return void
|
||||
*/
|
||||
public function retryError( $details )
|
||||
{
|
||||
throw new \IPS\nexus\Hosting\Exception( $this, 'DUMMY_SERVER' );
|
||||
}
|
||||
}
|
||||
Whitespace-only changes.
@@ -927,6 +927,14 @@ class _Invoice extends \IPS\Patterns\ActiveRecord
|
||||
$tax = $tax->add( $amount->amount );
|
||||
}
|
||||
$return['taxTotal'] = new \IPS\nexus\Money( $tax, $this->currency );
|
||||
|
||||
/* Discounts applied */
|
||||
$discount = new \IPS\Math\Number( '0' );
|
||||
foreach( $couponTaxReductions as $itemId => $amount )
|
||||
{
|
||||
$discount = $discount->add( $amount );
|
||||
}
|
||||
$return['discount'] = new \IPS\nexus\Money( $discount, $this->currency );
|
||||
|
||||
/* Add it all together */
|
||||
$return['total'] = new \IPS\nexus\Money( $subtotal->add( $shipping )->add( $tax ), $this->currency );
|
||||
@@ -1408,7 +1416,7 @@ class _Invoice extends \IPS\Patterns\ActiveRecord
|
||||
$return['delete'] = array(
|
||||
'title' => 'delete',
|
||||
'icon' => 'times-circle',
|
||||
'link' => $url->setQueryString( 'do', 'delete' )->csrf(),
|
||||
'link' => $url->setQueryString( 'do', 'delete' )->csrf()->getSafeUrlFromFilters(),
|
||||
'data' => array( 'confirm' => '', 'confirmSubMessage' => \IPS\Member::loggedIn()->language()->addToStack('invoice_delete_warning') )
|
||||
);
|
||||
}
|
||||
@@ -1605,7 +1613,18 @@ class _Invoice extends \IPS\Patterns\ActiveRecord
|
||||
}
|
||||
}
|
||||
$this->member = $memberToSave;
|
||||
$postBeforeRegister = isset( $this->guest_data['pbr'] ) ? $this->guest_data['pbr'] : NULL;
|
||||
|
||||
/* Get the PBR data */
|
||||
$postBeforeRegister = NULL;
|
||||
if( isset( $this->guest_data['pbr'] ) )
|
||||
{
|
||||
try
|
||||
{
|
||||
$postBeforeRegister = \IPS\Db::i()->select( '*', 'core_post_before_registering', array( 'secret=?', $this->guest_data['pbr'] ) )->first();
|
||||
}
|
||||
catch ( \UnderflowException $e ){}
|
||||
}
|
||||
|
||||
$referredBy = isset( $this->guest_data['referred_by'] ) ? $this->guest_data['referred_by'] : NULL;
|
||||
|
||||
$this->guest_data = isset( $this->guest_data['guestTransactionKey'] ) ? array( 'guestTransactionKey' => $this->guest_data['guestTransactionKey'] ) : NULL;
|
||||
|
||||
@@ -61,7 +61,7 @@ class _ItemsIterator extends \ArrayIterator
|
||||
static::$classnames[ $ext::$application ][ $ext::$type ] = $ext;
|
||||
}
|
||||
}
|
||||
if ( $data['app'] === 'nexus' and \in_array( $data['type'], array( 'product', 'hosting', 'ad' ) ) )
|
||||
if ( $data['app'] === 'nexus' and \in_array( $data['type'], array( 'product', 'ad' ) ) )
|
||||
{
|
||||
$data['type'] = 'package';
|
||||
}
|
||||
|
||||
@@ -244,6 +244,18 @@ class _CustomField extends \IPS\CustomField
|
||||
|
||||
return \IPS\Theme::i()->getTemplate( 'forms', 'nexus', 'global' )->usernamePasswordDisplay( $value );
|
||||
}
|
||||
|
||||
/* Select boxes store the value and not the index, but the parent method uses the index to determine the value.
|
||||
When using numeric options, this causes the wrong value to be returned. */
|
||||
if( $this->type == 'Select' )
|
||||
{
|
||||
if( $this->multiple )
|
||||
{
|
||||
return implode( ( $separator ) ? $separator : '<br>', explode( ',', htmlspecialchars( $value, ENT_DISALLOWED | ENT_QUOTES, 'UTF-8', FALSE ) ) );
|
||||
}
|
||||
|
||||
return htmlspecialchars( $value, ENT_DISALLOWED | ENT_QUOTES, 'UTF-8', FALSE );
|
||||
}
|
||||
|
||||
return parent::displayValue( $value, $showSensitiveInformation );
|
||||
}
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Dedicated Server Package
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
* @package Invision Community
|
||||
* @subpackage Nexus
|
||||
* @since 29 Apr 2014
|
||||
*/
|
||||
|
||||
namespace IPS\nexus\Package;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dedicated Server Package
|
||||
*/
|
||||
class _Dedi extends \IPS\nexus\Package
|
||||
{
|
||||
/**
|
||||
* @brief Icon
|
||||
*/
|
||||
public static $icon = 'database';
|
||||
|
||||
/**
|
||||
* @brief Title
|
||||
*/
|
||||
public static $title = 'dedicated_server';
|
||||
|
||||
/**
|
||||
* Show Purchase Record?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function showPurchaseRecord()
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* On Purchase Generated
|
||||
*
|
||||
* @param \IPS\nexus\Purchase $purchase The purchase
|
||||
* @param \IPS\nexus\Invoice $invoice The invoice
|
||||
* @return void
|
||||
*/
|
||||
public function onPurchaseGenerated( \IPS\nexus\Purchase $purchase, \IPS\nexus\Invoice $invoice )
|
||||
{
|
||||
try
|
||||
{
|
||||
$server = \IPS\nexus\Hosting\Server::load( $purchase->name, 'server_hostname' );
|
||||
$server->dedicated = $purchase->id;
|
||||
$server->save();
|
||||
}
|
||||
catch ( \OutOfRangeException $e ) { }
|
||||
|
||||
return parent::onPurchaseGenerated( $purchase, $invoice );
|
||||
}
|
||||
|
||||
/**
|
||||
* On Purchase Canceled
|
||||
*
|
||||
* @param \IPS\nexus\Purchase $purchase The purchase
|
||||
* @return void
|
||||
*/
|
||||
public function onCancel( \IPS\nexus\Purchase $purchase )
|
||||
{
|
||||
try
|
||||
{
|
||||
$server = \IPS\nexus\Hosting\Server::load( $purchase->id, 'server_dedicated' );
|
||||
$server->dedicated = 0;
|
||||
$server->save();
|
||||
}
|
||||
catch ( \OutOfRangeException $e ) { }
|
||||
|
||||
return parent::onCancel( $purchase );
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -359,7 +359,13 @@ class _Item extends \IPS\Content\Item implements \IPS\Content\Featurable, \IPS\C
|
||||
$titles = array();
|
||||
foreach ( \IPS\Lang::languages() as $lang )
|
||||
{
|
||||
$titles[] = $lang->get("nexus_package_{$this->id}");
|
||||
try
|
||||
{
|
||||
$titles[] = $lang->get( "nexus_package_{$this->id}" );
|
||||
}
|
||||
catch( \UnderflowException $e )
|
||||
{
|
||||
}
|
||||
}
|
||||
return implode( ' ', $titles );
|
||||
}
|
||||
|
||||
@@ -83,8 +83,7 @@ class _Package extends \IPS\Node\Model
|
||||
{
|
||||
return array(
|
||||
'Product' => 'IPS\nexus\Package\Product',
|
||||
'Ad' => 'IPS\nexus\Package\Ad',
|
||||
'Hosting' => 'IPS\nexus\Package\Hosting',
|
||||
'Ad' => 'IPS\nexus\Package\Ad'
|
||||
);
|
||||
}
|
||||
|
||||
@@ -100,14 +99,15 @@ class _Package extends \IPS\Node\Model
|
||||
public static function constructFromData( $data, $updateMultitonStoreIfExists = TRUE )
|
||||
{
|
||||
$types = static::packageTypes();
|
||||
$classname = $types[ mb_ucfirst( $data['p_type'] ) ];
|
||||
if ( isset( $classname::$packageDatabaseTable ) )
|
||||
$classname = isset( $types[ mb_ucfirst( $data['p_type'] ) ] ) ? $types[ mb_ucfirst( $data['p_type'] ) ] : '';
|
||||
|
||||
if ( $classname and isset( $classname::$packageDatabaseTable ) )
|
||||
{
|
||||
$data = array_merge( $data, \IPS\Db::i()->select( '*', $classname::$packageDatabaseTable, array( 'p_id=?', $data['p_id'] ) )->first() );
|
||||
}
|
||||
|
||||
/* Initiate an object */
|
||||
$obj = new $classname;
|
||||
$obj = ( $classname ) ? new $classname : new \IPS\nexus\Package;
|
||||
$obj->_new = FALSE;
|
||||
|
||||
/* Import data */
|
||||
@@ -230,8 +230,12 @@ class _Package extends \IPS\Node\Model
|
||||
{
|
||||
$this->item()->delete();
|
||||
parent::delete();
|
||||
\IPS\Db::i()->delete( static::$packageDatabaseTable, array( 'p_id=?', $this->id ) );
|
||||
|
||||
if( isset( static::$packageDatabaseTable ) )
|
||||
{
|
||||
\IPS\Db::i()->delete( static::$packageDatabaseTable, array( 'p_id=?', $this->id ) );
|
||||
}
|
||||
|
||||
/* Translatables */
|
||||
foreach ( array( '_assoc', '_page', '_email_purchase_subject', '_email_expire_soon_subject', '_email_expire_subject' ) as $suffix )
|
||||
{
|
||||
@@ -746,6 +750,11 @@ class _Package extends \IPS\Node\Model
|
||||
if ( \count( $renewOptions ) === 1 and isset( $renewOptions[0]['cost'][ $currency ] ) and $renewOptions[0]['cost'][ $currency ]['amount'] == $price and !$renewOptions[0]['add'] )
|
||||
{
|
||||
$primaryPrice = new \IPS\nexus\Purchase\RenewalTerm( $primaryPrice, new \DateInterval( "P{$renewOptions[0]['term']}" . mb_strtoupper( $renewOptions[0]['unit'] ) ), $tax );
|
||||
|
||||
if ( $initialTerm !== NULL )
|
||||
{
|
||||
$initialTerm = ( new \IPS\nexus\Purchase\RenewalTerm( [], $initialTerm ) )->getTermUnit();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1871,13 +1880,27 @@ class _Package extends \IPS\Node\Model
|
||||
}
|
||||
|
||||
$key = 'x';
|
||||
$childPurchaseRenewalCost = NULL;
|
||||
if ( $purchase->renewals )
|
||||
{
|
||||
/* If the purchase has any child purchases, subtract them from the renewal cost to find the actual cost of this product */
|
||||
$thisPurchaseRenewalCost = $purchase->renewals->cost->amount;
|
||||
$childPurchaseRenewalCost = new \IPS\Math\Number( "0" );
|
||||
foreach( $purchase->children() as $child )
|
||||
{
|
||||
if( $child->renewals )
|
||||
{
|
||||
$childPurchaseRenewalCost = $childPurchaseRenewalCost->add( $child->renewals->cost->amount );
|
||||
$thisPurchaseRenewalCost = $thisPurchaseRenewalCost->subtract( $child->renewals->cost->amount );
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( $changes['renew_options'] as $k => $data )
|
||||
{
|
||||
if ( $data['old'] != 'x' )
|
||||
{
|
||||
if ( $purchase->renewals->cost->amount->compare( new \IPS\Math\Number( number_format( $data['old']['cost'][ $purchase->renewals->cost->currency ]['amount'], \IPS\nexus\Money::numberOfDecimalsForCurrency( $purchase->renewals->cost->currency ), '.', '' ) ) ) === 0 )
|
||||
/* Compare against the actual renewal cost */
|
||||
if ( $thisPurchaseRenewalCost->compare( new \IPS\Math\Number( number_format( $data['old']['cost'][ $purchase->renewals->cost->currency ]['amount'], \IPS\nexus\Money::numberOfDecimalsForCurrency( $purchase->renewals->cost->currency ), '.', '' ) ) ) === 0 )
|
||||
{
|
||||
$term = $purchase->renewals->getTerm();
|
||||
if ( $data['old']['term'] == $term['term'] and $data['old']['unit'] == $term['unit'] )
|
||||
@@ -1965,13 +1988,38 @@ class _Package extends \IPS\Node\Model
|
||||
}
|
||||
|
||||
$currency = $purchase->renewal_currency ?: $purchase->member->defaultCurrency( );
|
||||
|
||||
$originalRenewalCost = $purchase->renewals->cost->amount;
|
||||
|
||||
/* If we had child purchases, add back the renewal amounts */
|
||||
$newRenewalCost = new \IPS\Math\Number( $newRenewTerms[ $key ]['cost'][ $currency ]['amount'] );
|
||||
if( $childPurchaseRenewalCost !== NULL )
|
||||
{
|
||||
$newRenewalCost = $newRenewalCost->add( $childPurchaseRenewalCost );
|
||||
}
|
||||
|
||||
$purchase->renewals = new \IPS\nexus\Purchase\RenewalTerm(
|
||||
new \IPS\nexus\Money( $newRenewTerms[ $key ]['cost'][ $currency ]['amount'], $currency ),
|
||||
new \IPS\nexus\Money( $newRenewalCost, $currency ),
|
||||
new \DateInterval( 'P' . $newRenewTerms[ $key ]['term'] . mb_strtoupper( $newRenewTerms[ $key ]['unit'] ) ),
|
||||
$tax
|
||||
);
|
||||
$purchase->save();
|
||||
|
||||
/* If we have a parent purchase, update the renewals for the parent */
|
||||
if( $parent = $purchase->parent() )
|
||||
{
|
||||
if( $parentRenewals = $parent->renewals )
|
||||
{
|
||||
$parentRenewalCost = $parentRenewals->cost->amount->subtract( $originalRenewalCost );
|
||||
$parentRenewalCost = $parentRenewalCost->add( $newRenewalCost );
|
||||
$parent->renewals = new \IPS\nexus\Purchase\RenewalTerm(
|
||||
new \IPS\nexus\Money( $parentRenewalCost, $currency ),
|
||||
$parentRenewals->interval,
|
||||
$parentRenewals->tax
|
||||
);
|
||||
$parent->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3528,7 +3576,7 @@ class _Package extends \IPS\Node\Model
|
||||
|
||||
/* If we're still in the initial period of the new package */
|
||||
$diff = \IPS\DateTime::create()->diff( $purchase->start->add( $initial ) );
|
||||
if( $diff->days > 0 )
|
||||
if( !$diff->invert and $diff->days > 0 )
|
||||
{
|
||||
$purchase->expire = $purchase->start->add( $diff );
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ abstract class _Payout extends \IPS\Patterns\ActiveRecord
|
||||
const STATUS_COMPLETE = 'done';
|
||||
const STATUS_PENDING = 'pend';
|
||||
const STATUS_CANCELED = 'canc';
|
||||
const STATUS_PROCESSING = 'wait';
|
||||
|
||||
/**
|
||||
* @brief Multiton Store
|
||||
@@ -318,6 +319,23 @@ abstract class _Payout extends \IPS\Patterns\ActiveRecord
|
||||
{
|
||||
return \IPS\Http\Url::internal( "app=nexus&module=payments&controller=payouts&do=view&id={$this->id}", 'admin' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the payout as completed.
|
||||
* Moved this out of the controllers because there are times when
|
||||
* the payout may not be processed immediately (e.g. via PayPal batch)
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function markCompleted()
|
||||
{
|
||||
$this->status = static::STATUS_COMPLETE;
|
||||
$this->completed = new \IPS\DateTime;
|
||||
$this->save();
|
||||
|
||||
/* Notify member */
|
||||
\IPS\Email::buildFromTemplate( 'nexus', 'payoutComplete', array( $this ), \IPS\Email::TYPE_TRANSACTIONAL )->send( $this->member );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get output for API
|
||||
|
||||
@@ -745,16 +745,22 @@ class _Package extends \IPS\Node\Model
|
||||
$numberOfDaysInCurrentRenewalTerm = new \IPS\Math\Number( (string) round( $numberOfDaysInCurrentRenewalTerm ) );
|
||||
|
||||
/* Count Days in Period */
|
||||
$daysUsedInPeriod = \IPS\DateTime::intervalToDays( $timeSinceStartOfCurrentPeriod );
|
||||
$daysRemainingInPeriod = new \IPS\Math\Number( (string) $numberOfDaysInCurrentRenewalTerm->subtract( new \IPS\Math\Number( (string) $daysUsedInPeriod ) ) );
|
||||
$daysUsedInPeriod = new \IPS\Math\Number( (string)\IPS\DateTime::intervalToDays( $timeSinceStartOfCurrentPeriod ) );
|
||||
$daysRemainingInPeriod = new \IPS\Math\Number( (string) $numberOfDaysInCurrentRenewalTerm->subtract( $daysUsedInPeriod ) );
|
||||
|
||||
/* Currency Worth of value used in current sub */
|
||||
$valueUsedOfSubscription = $purchase->renewals->cost->amount->divide( $numberOfDaysInCurrentRenewalTerm )->multiply( $daysRemainingInPeriod );
|
||||
$valueUsedOfSubscription = $purchase->renewals->cost->amount->divide( $numberOfDaysInCurrentRenewalTerm )->multiply( $daysUsedInPeriod );
|
||||
|
||||
if ( !$daysRemainingInPeriod->isGreaterThanZero() )
|
||||
{
|
||||
return new \IPS\nexus\Money( new \IPS\Math\Number('0'), $currency );
|
||||
}
|
||||
elseif( $priceOfExistingPackage > $priceOfThisPackage )
|
||||
{
|
||||
/* If this is a downgrade, calculate a refund */
|
||||
$refund = $purchase->renewals->cost->amount->subtract( $valueUsedOfSubscription );
|
||||
return new \IPS\nexus\Money( $refund->multiply( new \IPS\Math\Number( "-1" ) ), $currency );
|
||||
}
|
||||
else
|
||||
{
|
||||
return new \IPS\nexus\Money( $renewalTermToUse->cost->amount->subtract( $valueUsedOfSubscription ), $currency );
|
||||
@@ -887,7 +893,7 @@ class _Package extends \IPS\Node\Model
|
||||
if( $costToUpgrade->amount->isGreaterThanZero() AND \IPS\Settings::i()->nexus_subs_upgrade > 0 )
|
||||
{
|
||||
$startOfCurrentPeriod = $purchase->expire->sub( new \DateInterval( 'P' . $oldTerm['term'] . mb_strtoupper( $oldTerm['unit'] ) ) );
|
||||
$purchase->expire = $startOfCurrentPeriod < $purchase->start ? $startOfCurrentPeriod->add( $term->interval ) : $purchase->start->add( $term->interval );
|
||||
$purchase->expire = $startOfCurrentPeriod < $purchase->start ? $purchase->start->add( $term->interval ) : $startOfCurrentPeriod->add( $term->interval );
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -459,7 +459,7 @@ class _Transaction extends \IPS\Patterns\ActiveRecord
|
||||
*/
|
||||
public function get_billing_agreement()
|
||||
{
|
||||
return $this->_data['billing_agreement'] ? \IPS\nexus\Customer\BillingAgreement::load( $this->_data['billing_agreement'] ) : NULL;
|
||||
return ( isset( $this->_data['billing_agreement'] ) AND $this->_data['billing_agreement'] ) ? \IPS\nexus\Customer\BillingAgreement::load( $this->_data['billing_agreement'] ) : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in new issue
Block a user