Version 4.7.0
This commit is contained in:
1 parent
517a5e1f70
commit
8ba7d43898
1933 files changed
+65613
-11332
No files matched your search
@@ -209,7 +209,7 @@ class _Braintree extends \IPS\nexus\Gateway
|
||||
'line1' => isset( $invoice->shipaddress->addressLines[0] ) ? $invoice->shipaddress->addressLines[0] : NULL,
|
||||
'line2' => isset( $invoice->shipaddress->addressLines[1] ) ? $invoice->shipaddress->addressLines[1] : NULL,
|
||||
'city' => $invoice->shipaddress->city,
|
||||
'state' => $invoice->shipaddress->region,
|
||||
'state' => $settings['type'] === 'paypal' ? $this->_payPalState( $invoice->shipaddress ) : $invoice->shipaddress->region,
|
||||
'postalCode' => $invoice->shipaddress->postalCode,
|
||||
'countryCode' => $invoice->shipaddress->country,
|
||||
'phone' => $invoice->member->cm_phone,
|
||||
@@ -259,7 +259,7 @@ class _Braintree extends \IPS\nexus\Gateway
|
||||
'lastName' => $invoice->member->cm_last_name,
|
||||
'streetAddress' => $invoice->billaddress ? implode( ', ', $invoice->billaddress->addressLines ) : NULL,
|
||||
'locality' => $invoice->billaddress ? $invoice->billaddress->city : NULL,
|
||||
'region' => $invoice->billaddress ? $invoice->billaddress->region : NULL,
|
||||
'region' => $invoice->billaddress ? ( $settings['type'] === 'paypal' ? $this->_payPalState( $invoice->billaddress ) : $invoice->billaddress->region ) : NULL,
|
||||
'postalCode' => $invoice->billaddress ? $invoice->billaddress->postalCode : NULL,
|
||||
'countryCodeAlpha2' => $invoice->billaddress ? $invoice->billaddress->country : NULL,
|
||||
'phoneNumber' => preg_replace( '/[^0-9]/', '', $invoice->member->cm_phone ),
|
||||
@@ -358,7 +358,7 @@ class _Braintree extends \IPS\nexus\Gateway
|
||||
'lastName' => $transaction->invoice->member->cm_last_name,
|
||||
'locality' => $transaction->invoice->billaddress->city,
|
||||
'postalCode' => $transaction->invoice->billaddress->postalCode,
|
||||
'region' => $transaction->invoice->billaddress->region,
|
||||
'region' => $settings['type'] === 'paypal' ? $this->_payPalState( $transaction->invoice->billaddress ) : $transaction->invoice->billaddress->region,
|
||||
'streetAddress' => implode( ', ', $transaction->invoice->billaddress->addressLines )
|
||||
);
|
||||
|
||||
@@ -392,7 +392,7 @@ class _Braintree extends \IPS\nexus\Gateway
|
||||
'lastName' => $transaction->invoice->member->cm_last_name,
|
||||
'locality' => $transaction->invoice->shipaddress->city,
|
||||
'postalCode' => $transaction->invoice->shipaddress->postalCode,
|
||||
'region' => $transaction->invoice->shipaddress->region,
|
||||
'region' => $settings['type'] === 'paypal' ? $this->_payPalState( $transaction->invoice->billaddress ) : $transaction->invoice->billaddress->region,
|
||||
'streetAddress' => implode( ', ', $transaction->invoice->shipaddress->addressLines )
|
||||
);
|
||||
|
||||
@@ -985,5 +985,28 @@ class _Braintree extends \IPS\nexus\Gateway
|
||||
return $errorMessage;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get short code for PayPal State
|
||||
*
|
||||
* @param \IPS\GeoLocation $address
|
||||
* @return string
|
||||
*/
|
||||
protected function _payPalState( \IPS\GeoLocation $address ): string
|
||||
{
|
||||
/* PayPal requires short codes for states */
|
||||
$state = $address->region;
|
||||
if ( isset( \IPS\nexus\Customer\Address::$stateCodes[ $address->country ] ) )
|
||||
{
|
||||
if ( !array_key_exists( $state, \IPS\nexus\Customer\Address::$stateCodes[ $address->country ] ) )
|
||||
{
|
||||
$_state = array_search( $address->region, \IPS\nexus\Customer\Address::$stateCodes[ $address->country ] );
|
||||
if ( $_state !== FALSE )
|
||||
{
|
||||
$state = $_state;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $state;
|
||||
}
|
||||
}
|
||||
@@ -183,7 +183,7 @@ class _Gateway extends \IPS\Node\Model
|
||||
*/
|
||||
public function form( &$form )
|
||||
{
|
||||
$form->add( new \IPS\Helpers\Form\Translatable( 'paymethod_name', NULL, TRUE, array( 'app' => 'nexus', 'key' => "nexus_paymethod_{$this->id}" ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Translatable( 'paymethod_name', NULL, TRUE, array( 'app' => 'nexus', 'key' => $this->id ? "nexus_paymethod_{$this->id}" : NULL ) ) );
|
||||
$this->settings( $form );
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'paymethod_countries', ( $this->id and $this->countries !== '*' ) ? explode( ',', $this->countries ) : '*', FALSE, array( 'options' => array_map( function( $val )
|
||||
{
|
||||
@@ -210,6 +210,8 @@ class _Gateway extends \IPS\Node\Model
|
||||
$values['countries'] = \is_array( $values['paymethod_countries'] ) ? implode( ',', $values['paymethod_countries'] ) : $values['paymethod_countries'];
|
||||
}
|
||||
|
||||
$values['validationfile'] = (string) $values['m_validationfile'];
|
||||
|
||||
$settings = array();
|
||||
foreach ( $values as $k => $v )
|
||||
{
|
||||
@@ -217,7 +219,7 @@ class _Gateway extends \IPS\Node\Model
|
||||
{
|
||||
$settings[ mb_substr( $k, mb_strlen( $this->gateway ) + 1 ) ] = $v;
|
||||
}
|
||||
if( $k != "countries" )
|
||||
if( $k != "countries" AND $k != 'validationfile' )
|
||||
{
|
||||
unset( $values[$k] );
|
||||
}
|
||||
@@ -360,6 +362,21 @@ class _Gateway extends \IPS\Node\Model
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* [Node] Dissalow gateways with validation files to be copyable ( the file can't /shouldn't be copied and reusing the existing value will delete the source file when the new copy is deleted )
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function canCopy()
|
||||
{
|
||||
if ( $this->deleteOrMoveQueued() === TRUE )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return ( !$this->validationfile );
|
||||
}
|
||||
|
||||
/**
|
||||
* [ActiveRecord] Delete
|
||||
*
|
||||
@@ -370,7 +387,14 @@ class _Gateway extends \IPS\Node\Model
|
||||
/* Delete cards and billing agreements */
|
||||
\IPS\Db::i()->delete( 'nexus_customer_cards', array( 'card_method=?', $this->id ) );
|
||||
\IPS\Db::i()->delete( 'nexus_billing_agreements', array( 'ba_method=?', $this->id ) );
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
\IPS\File::get( 'nexus_Gateways', $this->validationfile )->delete();
|
||||
}
|
||||
catch( \Exception $ex ) { }
|
||||
|
||||
|
||||
/* Delete */
|
||||
parent::delete();
|
||||
|
||||
@@ -635,4 +659,27 @@ class _Gateway extends \IPS\Node\Model
|
||||
'name' => $this->_title
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Apple Pay domain verification file if there's one.
|
||||
*
|
||||
* @return void|null
|
||||
*/
|
||||
public static function getStripeAppleVerificationFile()
|
||||
{
|
||||
foreach ( static::roots() as $gateway )
|
||||
{
|
||||
if ( $gateway->gateway == 'Stripe' AND $gateway->validationfile )
|
||||
{
|
||||
try
|
||||
{
|
||||
return \IPS\File::get( 'nexus_Gateways', $gateway->validationfile );
|
||||
}
|
||||
catch ( \Exception $e )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -118,9 +118,38 @@ class _BillingAgreement extends \IPS\nexus\Customer\BillingAgreement
|
||||
*/
|
||||
public function latestUnclaimedTransaction()
|
||||
{
|
||||
$url = "payments/billing-agreements/{$this->gw_id}/transactions?start_date=" . date('Y-m-d', time() - (86400 * 31)) . '&end_date=' . date('Y-m-d');
|
||||
try
|
||||
$data = $this->_getData();
|
||||
/* PayPal Subscriptions */
|
||||
if ( isset( $data['plan_id'] ) )
|
||||
{
|
||||
$transactions = $this->method->api( "billing/subscriptions/{$this->gw_id}/transactions?start_time=" . \IPS\DateTime::create()->sub( new \DateInterval( 'P31D' ) )->rfc3339() . '&end_time=' . \IPS\DateTime::create()->rfc3339(), NULL, 'get' );
|
||||
foreach ( array_reverse( $transactions['transactions'] ) as $t )
|
||||
{
|
||||
if ( $t['status'] == 'COMPLETED' )
|
||||
{
|
||||
try
|
||||
{
|
||||
\IPS\Db::i()->select( 't_id', 'nexus_transactions', array( 't_method=? AND t_gw_id=?', $this->method->id, $t['id'] ) )->first();
|
||||
}
|
||||
catch ( \UnderflowException $e )
|
||||
{
|
||||
$transaction = new \IPS\nexus\Transaction;
|
||||
$transaction->member = $this->member;
|
||||
$transaction->method = $this->method;
|
||||
$transaction->amount = new \IPS\nexus\Money( $t['amount_with_breakdown']['gross_amount']['value'], $t['amount_with_breakdown']['gross_amount']['currency_code'] );
|
||||
$transaction->date = new \IPS\DateTime( $t['time'] );
|
||||
$transaction->extra = array( 'automatic' => TRUE );
|
||||
$transaction->gw_id = $t['id'];
|
||||
$transaction->billing_agreement = $this;
|
||||
return $transaction;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Legacy Billing Agreement */
|
||||
else
|
||||
{
|
||||
$url = "payments/billing-agreements/{$this->gw_id}/transactions?start_date=" . date( 'Y-m-d', time() - ( 86400 * 31 ) ) . '&end_date=' . date( 'Y-m-d' );
|
||||
$transactions = $this->method->api( $url, NULL, 'get' );
|
||||
foreach ( array_reverse( $transactions['agreement_transaction_list'] ) as $t )
|
||||
{
|
||||
@@ -128,7 +157,7 @@ class _BillingAgreement extends \IPS\nexus\Customer\BillingAgreement
|
||||
{
|
||||
try
|
||||
{
|
||||
$existingTransaction = \IPS\Db::i()->select( 't_id', 'nexus_transactions', array( 't_method=? AND t_gw_id=?', $this->method->id, $t['transaction_id'] ) )->first();
|
||||
\IPS\Db::i()->select( 't_id', 'nexus_transactions', array( 't_method=? AND t_gw_id=?', $this->method->id, $t['transaction_id'] ) )->first();
|
||||
}
|
||||
catch ( \UnderflowException $e )
|
||||
{
|
||||
@@ -145,10 +174,6 @@ class _BillingAgreement extends \IPS\nexus\Customer\BillingAgreement
|
||||
}
|
||||
}
|
||||
}
|
||||
catch ( \IPS\Http\Url\Exception $e )
|
||||
{
|
||||
throw $e;
|
||||
}
|
||||
|
||||
throw new \OutOfRangeException( "{$this->id} ({$this->gw_id})\n{$url}\n\n" . print_r( $transactions, TRUE ) );
|
||||
}
|
||||
|
||||
@@ -47,12 +47,6 @@ class _PayPal extends \IPS\nexus\Gateway
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Billing agreements require a billing address */
|
||||
elseif ( static::_canProcessRecurringsAsBillingAgreement( $recurrings ) and ( isset( $settings['billing_agreements'] ) and $settings['billing_agreements'] == 'required' ) and !$billingAddress )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Check transaction limit */
|
||||
switch ( $amount->currency )
|
||||
{
|
||||
@@ -538,7 +532,7 @@ class _PayPal extends \IPS\nexus\Gateway
|
||||
'description' => $description,
|
||||
'billing_cycles' => array(),
|
||||
'payment_preferences' => array(
|
||||
'auto_bill_outstanding' => TRUE,
|
||||
'auto_bill_outstanding' => FALSE,
|
||||
'payment_failure_threshold' => \intval( $settings['billing_agreement_allowed_fails'] )
|
||||
),
|
||||
'taxes' => array(
|
||||
@@ -618,29 +612,36 @@ 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
|
||||
been processed yet, cancel the billing agreement */
|
||||
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();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Try to find the authorization ID */
|
||||
if ( mb_substr( $transaction->gw_id, 0, 4 ) === 'PAY-' )
|
||||
{
|
||||
$authId = NULL;
|
||||
$payment = $this->api( "payments/payment/{$transaction->gw_id}", NULL, 'get' );
|
||||
foreach ( $payment['transactions'][0]['related_resources'] as $rr )
|
||||
try
|
||||
{
|
||||
if ( isset( $rr['authorization'] ) )
|
||||
$payment = $this->api( "payments/payment/{$transaction->gw_id}", NULL, 'get' );
|
||||
foreach ( $payment['transactions'][0]['related_resources'] as $rr )
|
||||
{
|
||||
$authId = $rr['authorization']['id'];
|
||||
if ( isset( $rr['authorization'] ) )
|
||||
{
|
||||
$authId = $rr['authorization']['id'];
|
||||
}
|
||||
}
|
||||
|
||||
if ( !$authId )
|
||||
{
|
||||
throw new \RuntimeException;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !$authId )
|
||||
/* Let's try the gateway id as the auth id */
|
||||
catch ( \IPS\nexus\Gateway\PayPal\Exception $e )
|
||||
{
|
||||
throw new \RuntimeException;
|
||||
$authId = $transaction->gw_id;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -683,24 +684,53 @@ class _PayPal extends \IPS\nexus\Gateway
|
||||
if ( $billingAgreement = $transaction->billing_agreement )
|
||||
{
|
||||
$response = 'WAIT';
|
||||
$transactions = $this->api( "payments/billing-agreements/{$transaction->billing_agreement->gw_id}/transactions?start_date=" . date( 'Y-m-d', $transaction->date->getTimestamp() - 86400 ) . '&end_date=' . date( 'Y-m-d' ), NULL, 'get' );
|
||||
foreach ( $transactions['agreement_transaction_list'] as $t )
|
||||
/* Subscriptions API */
|
||||
if( $data = $billingAgreement->_getData() AND isset( $data['plan_id'] ) )
|
||||
{
|
||||
if ( ( !$transaction->gw_id or $transaction->gw_id == $t['transaction_id'] ) and $transaction->amount->currency == $t['amount']['currency'] and $transaction->amount->amountAsString() == $t['amount']['value'] )
|
||||
{
|
||||
if ( $t['status'] == 'Completed' )
|
||||
$transactions = $this->api( "billing/subscriptions/{$transaction->billing_agreement->gw_id}/transactions?start_time=" . \IPS\DateTime::ts( $transaction->date->getTimestamp() - 86400 )->rfc3339() . '&end_time=' . \IPS\DateTime::ts( time() )->rfc3339(), NULL, 'get' );
|
||||
foreach ( $transactions['transactions'] as $t )
|
||||
{
|
||||
if ( ( !$transaction->gw_id or $transaction->gw_id == $t['id'] ) and $transaction->amount->currency == $t['amount_with_breakdown']['gross_amount']['currency_code'] and $transaction->amount->amountAsString() == $t['amount_with_breakdown']['gross_amount']['value'] )
|
||||
{
|
||||
$transaction->gw_id = $t['transaction_id'];
|
||||
$transaction->save();
|
||||
return TRUE;
|
||||
if ( $t['status'] == 'COMPLETED' )
|
||||
{
|
||||
$transaction->gw_id = $t['id'];
|
||||
$transaction->save();
|
||||
return TRUE;
|
||||
}
|
||||
elseif ( $t['status'] == 'REFUNDED' )
|
||||
{
|
||||
$response = 'RFND';
|
||||
}
|
||||
elseif ( $t['status'] == 'DECLINED' )
|
||||
{
|
||||
$response = 'FAIL';
|
||||
}
|
||||
}
|
||||
elseif ( $t['status'] == 'Refunded' )
|
||||
}
|
||||
}
|
||||
/* Legacy Billing Agreements */
|
||||
else
|
||||
{
|
||||
$transactions = $this->api( "payments/billing-agreements/{$transaction->billing_agreement->gw_id}/transactions?start_date=" . date( 'Y-m-d', $transaction->date->getTimestamp() - 86400 ) . '&end_date=' . date( 'Y-m-d' ), NULL, 'get' );
|
||||
foreach ( $transactions['agreement_transaction_list'] as $t )
|
||||
{
|
||||
if ( ( !$transaction->gw_id or $transaction->gw_id == $t['transaction_id'] ) and $transaction->amount->currency == $t['amount']['currency'] and $transaction->amount->amountAsString() == $t['amount']['value'] )
|
||||
{
|
||||
$response = 'RFND';
|
||||
}
|
||||
elseif ( $t['status'] == 'Failed' )
|
||||
{
|
||||
$response = 'FAIL';
|
||||
if ( $t['status'] == 'Completed' )
|
||||
{
|
||||
$transaction->gw_id = $t['transaction_id'];
|
||||
$transaction->save();
|
||||
return TRUE;
|
||||
}
|
||||
elseif ( $t['status'] == 'Refunded' )
|
||||
{
|
||||
$response = 'RFND';
|
||||
}
|
||||
elseif ( $t['status'] == 'Failed' )
|
||||
{
|
||||
$response = 'FAIL';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -781,9 +811,21 @@ class _PayPal extends \IPS\nexus\Gateway
|
||||
{
|
||||
/* The capture ID is *normally* the gateway transaction ID */
|
||||
$captureId = $transaction->gw_id;
|
||||
|
||||
|
||||
/* Refund Amount */
|
||||
$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-' )
|
||||
{
|
||||
$response = $this->api( "payments/sale/{$transaction->gw_id}/refund", array( 'amount' => array(
|
||||
'currency' => $amount->currency,
|
||||
'total' => $amount->amountAsString()
|
||||
) ) );
|
||||
return $response['id'];
|
||||
}
|
||||
/* But if it starts with I- (or is a blank but known to be a billing agreement payment) - that's a billing agreement */
|
||||
if ( ( $transaction->billing_agreement and !$transaction->gw_id ) or ( mb_substr( $transaction->gw_id, 0, 2 ) === 'I-' ) )
|
||||
elseif ( ( $transaction->billing_agreement and !$transaction->gw_id ) or ( mb_substr( $transaction->gw_id, 0, 2 ) === 'I-' ) )
|
||||
{
|
||||
$transactions = $this->api( "payments/billing-agreements/{$transaction->billing_agreement->gw_id}/transactions?start_date=" . $transaction->date->sub( new \DateInterval('P1D') )->format('Y-m-d') . '&end_date=' . $transaction->date->format('Y-m-d'), NULL, 'get' );
|
||||
foreach ( $transactions['agreement_transaction_list'] as $t )
|
||||
@@ -813,7 +855,6 @@ class _PayPal extends \IPS\nexus\Gateway
|
||||
}
|
||||
|
||||
/* Process Refund */
|
||||
$amount = $amount ? new \IPS\nexus\Money( $amount, $transaction->currency ) : $transaction->amount;
|
||||
$response = $this->api( "payments/capture/{$captureId}/refund", array( 'amount' => array(
|
||||
'currency' => $amount->currency,
|
||||
'total' => $amount->amountAsString()
|
||||
@@ -1157,8 +1198,11 @@ class _PayPal extends \IPS\nexus\Gateway
|
||||
if ( \count( $recurrings ) == 1 )
|
||||
{
|
||||
$recurrance = array_pop( $recurrings );
|
||||
|
||||
if ( $recurrance['dueDate']->getTimestamp() === \IPS\DateTime::create()->add( $recurrance['term']->interval )->getTimestamp() )
|
||||
|
||||
/* 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 )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -314,14 +314,6 @@ class _Stripe extends \IPS\nexus\Gateway
|
||||
'member' => $member,
|
||||
) ) );
|
||||
}
|
||||
elseif ( $settings['type'] === 'amex' )
|
||||
{
|
||||
return array( 'card' => new \IPS\Helpers\Form\Custom( $this->id . '_card', NULL, FALSE, array(
|
||||
'rowHtml' => function( $field ) use ( $settings ) {
|
||||
return \IPS\Theme::i()->getTemplate( 'forms', 'nexus', 'global' )->amexExpressCheckout( $field, $this, $settings['amex_client_id'] );
|
||||
}
|
||||
) ) );
|
||||
}
|
||||
elseif ( $settings['type'] === 'sofort' and \IPS\NEXUS_TEST_GATEWAYS )
|
||||
{
|
||||
return array(
|
||||
@@ -608,12 +600,6 @@ class _Stripe extends \IPS\nexus\Gateway
|
||||
'phone' => isset( $transaction->invoice->member->cm_phone ) ? $transaction->invoice->member->cm_phone : NULL
|
||||
);
|
||||
}
|
||||
|
||||
/* Amex Express Checkout */
|
||||
if ( $settings['type'] === 'amex' )
|
||||
{
|
||||
$data['card'] = $values[ $this->id . '_card' ];
|
||||
}
|
||||
|
||||
/* Source-based */
|
||||
elseif ( \is_string( $card ) )
|
||||
@@ -851,9 +837,9 @@ class _Stripe extends \IPS\nexus\Gateway
|
||||
{
|
||||
$response = $charge;
|
||||
$response['source'] = $response['payment_method_details'];
|
||||
foreach ( array( 'cvc_check', 'address_line1_check', 'address_zip_check' ) as $k )
|
||||
foreach ( array( 'cvc_check', 'address_line1_check', 'address_postal_code_check' ) as $k )
|
||||
{
|
||||
$response['source']['card'][ $k ] = isset( $response['payment_method_details']['checks'][ $k ] ) ? $response['payment_method_details']['checks'][ $k ] : 'unavailable';
|
||||
$response['source']['card'][ $k ] = isset( $response['payment_method_details']['card']['checks'][ $k ] ) ? $response['payment_method_details']['card']['checks'][ $k ] : 'unavailable';
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -975,7 +961,7 @@ class _Stripe extends \IPS\nexus\Gateway
|
||||
public function form( &$form )
|
||||
{
|
||||
$form->addHeader('stripe_basic_settings');
|
||||
$form->add( new \IPS\Helpers\Form\Translatable( 'paymethod_name', NULL, TRUE, array( 'app' => 'nexus', 'key' => "nexus_paymethod_{$this->id}" ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Translatable( 'paymethod_name', NULL, TRUE, array( 'app' => 'nexus', 'key' => $this->id ? "nexus_paymethod_{$this->id}" : NULL ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'paymethod_countries', ( $this->id and $this->countries !== '*' ) ? explode( ',', $this->countries ) : '*', FALSE, array( 'options' => array_map( function( $val )
|
||||
{
|
||||
return "country-{$val}";
|
||||
@@ -1002,7 +988,6 @@ class _Stripe extends \IPS\nexus\Gateway
|
||||
'card' => 'stripe_type_card',
|
||||
'native' => 'stripe_type_native',
|
||||
'alipay' => 'stripe_type_alipay',
|
||||
'amex' => 'stripe_type_amex',
|
||||
'bancontact'=> 'stripe_type_bancontact',
|
||||
'giropay' => 'stripe_type_giropay',
|
||||
'ideal' => 'stripe_type_ideal',
|
||||
@@ -1010,11 +995,17 @@ class _Stripe extends \IPS\nexus\Gateway
|
||||
),
|
||||
'toggles' => array(
|
||||
'card' => array( 'stripe_cards' ),
|
||||
'amex' => array( 'stripe_amex_client_id' )
|
||||
'native' => array( 'stripe_apple_pay_file')
|
||||
)
|
||||
) ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'stripe_cards', $settings ? $settings['cards'] : TRUE, FALSE, array(), NULL, NULL, NULL, 'stripe_cards' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'stripe_amex_client_id', ( $settings and isset( $settings['amex_client_id'] ) ) ? $settings['amex_client_id'] : NULL, NULL, array(), NULL, NULL, \IPS\Member::loggedIn()->language()->addToStack('stripe_amex_client_id_gen'), 'stripe_amex_client_id' ) );
|
||||
|
||||
if ( \IPS\CIC )
|
||||
{
|
||||
$form->add( new \IPS\Helpers\Form\Upload( 'm_validationfile', $this->validationfile ? \IPS\File::get( 'nexus_Gateways', $this->validationfile ) : '', FALSE, array( 'storageExtension' => 'nexus_Gateways' ), NULL, NULL, NULL, 'stripe_apple_pay_file' ) );
|
||||
\IPS\Member::loggedIn()->language()->words[ 'm_validationfile'] = \IPS\Member::loggedIn()->language()->addToStack('stripe_apple_verificationfile');
|
||||
}
|
||||
|
||||
$form->addHeader('stripe_webhook');
|
||||
$form->addMessage('stripe_webhook_blurb');
|
||||
\IPS\Member::loggedIn()->language()->words["stripe_webhook_blurb"] = sprintf( \IPS\Member::loggedIn()->language()->get('stripe_webhook_blurb'), (string) \IPS\Http\Url::internal( 'applications/nexus/interface/gateways/stripe.php', 'interface' ) );
|
||||
@@ -1058,7 +1049,7 @@ class _Stripe extends \IPS\nexus\Gateway
|
||||
}
|
||||
if ( !$webhookId )
|
||||
{
|
||||
throw new \DomainException( \IPS\Member::loggedIn()->language()->addToStack( 'stripe_webhook_invalid', FALSE, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->formatList( $this->webhookEvents ) ) ) ) );
|
||||
throw new \InvalidArgumentException( \IPS\Member::loggedIn()->language()->addToStack( 'stripe_webhook_invalid', FALSE, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->formatList( $this->webhookEvents ) ) ) ) );
|
||||
}
|
||||
$settings['webhook_id'] = $webhookId;
|
||||
|
||||
|
||||
@@ -1597,14 +1597,12 @@ class _Invoice extends \IPS\Patterns\ActiveRecord
|
||||
$memberToSave->$k = $v;
|
||||
}
|
||||
$memberToSave->save();
|
||||
|
||||
/* Was this a referral? */
|
||||
if( $this->guest_data['referred_by'] )
|
||||
{
|
||||
$referredBy = \IPS\Member::load( $this->guest_data['referred_by'] )->addReferral( $memberToSave );
|
||||
}
|
||||
|
||||
$memberToSave->logHistory( 'core', 'account', array( 'type' => 'register_checkout', 'spamCode' => $spamData['code'], 'spamAction' => $spamData['action'], 'invoice' => $this->id ), FALSE );
|
||||
|
||||
if( isset( $this->guest_data['agreed_terms'] ) and $this->guest_data['agreed_terms'] )
|
||||
{
|
||||
$memberToSave->logHistory( 'core', 'terms_acceptance', array( 'type' => 'terms' ) );
|
||||
}
|
||||
}
|
||||
$this->member = $memberToSave;
|
||||
$postBeforeRegister = isset( $this->guest_data['pbr'] ) ? $this->guest_data['pbr'] : NULL;
|
||||
@@ -1905,8 +1903,10 @@ class _Invoice extends \IPS\Patterns\ActiveRecord
|
||||
}
|
||||
|
||||
/* Referral Commission */
|
||||
$this->status_extra = $extra;
|
||||
if ( \IPS\Settings::i()->ref_on and $commission = $this->commission( $amountReceived ) and $commission['amount'] )
|
||||
{
|
||||
$extra = $this->status_extra; // Reload, the commission method may have changed this.
|
||||
$credits = $commission['referrer']->cm_credits;
|
||||
$credits[ $this->currency ]->amount = $credits[ $this->currency ]->amount->add( $commission['amount']->amount );
|
||||
$commission['referrer']->cm_credits = $credits;
|
||||
@@ -2093,6 +2093,7 @@ class _Invoice extends \IPS\Patterns\ActiveRecord
|
||||
public function markUnpaid( $status, \IPS\Member $member = NULL )
|
||||
{
|
||||
/* Do stuff */
|
||||
$extra = $this->status_extra;
|
||||
if ( $this->status === static::STATUS_PAID )
|
||||
{
|
||||
/* Loop items */
|
||||
@@ -2104,36 +2105,15 @@ class _Invoice extends \IPS\Patterns\ActiveRecord
|
||||
if ( $item->payTo and !$item->payTo->inGroup( explode( ',', \IPS\Settings::i()->nexus_no_commission ) ) )
|
||||
{
|
||||
$recipientAmounts = $item->recipientAmounts();
|
||||
|
||||
$credits = $item->payTo->cm_credits;
|
||||
$credits[ $this->currency ]->amount = $credits[ $this->currency ]->amount->subtract( $recipientAmounts['recipient_final']->amount );
|
||||
$item->payTo->cm_credits = $credits;
|
||||
$item->payTo->save();
|
||||
|
||||
$item->payTo->log( 'comission', array(
|
||||
'type' => 'invoice_refund',
|
||||
'currency' => $this->currency,
|
||||
'amount' => $recipientAmounts['recipient_final']->amount,
|
||||
'new_amount' => $credits[ $this->currency ]->amount,
|
||||
'member' => $this->member->member_id,
|
||||
'invoice_id' => $this->id,
|
||||
'invoice_title' => $this->title
|
||||
), FALSE );
|
||||
|
||||
\IPS\Email::buildFromTemplate( 'nexus', 'commissionRevoked', array( $this, $item, $recipientAmounts['recipient_final'] ), \IPS\Email::TYPE_TRANSACTIONAL )
|
||||
->send(
|
||||
$item->payTo,
|
||||
array_map(
|
||||
function( $contact )
|
||||
{
|
||||
return $contact->alt_id->email;
|
||||
},
|
||||
iterator_to_array( $item->payTo->alternativeContacts( array( 'billing=1' ) ) )
|
||||
),
|
||||
( ( \in_array( 'commission_earned', explode( ',', \IPS\Settings::i()->nexus_notify_copy_types ) ) AND \IPS\Settings::i()->nexus_notify_copy_email ) ? explode( ',', \IPS\Settings::i()->nexus_notify_copy_email ) : array() )
|
||||
);
|
||||
$this->revokeCommission( $item->payTo, $recipientAmounts['recipient_final'], $item );
|
||||
}
|
||||
}
|
||||
|
||||
/* Revoke commissions based on the referral rules */
|
||||
if( !empty( $extra['commissionref'] ) AND !empty( $extra['commissionamount'] ) )
|
||||
{
|
||||
$this->revokeCommission( \IPS\nexus\Customer::load( $extra['commissionref'] ), new \IPS\nexus\Money( $extra['commissionamount'], $this->currency ) );
|
||||
}
|
||||
|
||||
/* Delete purchases */
|
||||
foreach ( $this->purchasesCreated() as $purchase )
|
||||
@@ -2161,7 +2141,6 @@ class _Invoice extends \IPS\Patterns\ActiveRecord
|
||||
/* Log */
|
||||
if ( $member )
|
||||
{
|
||||
$extra = $this->status_extra;
|
||||
$extra['type'] = 'manual';
|
||||
$extra['setTo'] = $status;
|
||||
$extra['setByID'] = $member->member_id;
|
||||
@@ -2172,6 +2151,44 @@ class _Invoice extends \IPS\Patterns\ActiveRecord
|
||||
/* Save */
|
||||
$this->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Revoke Commission
|
||||
*
|
||||
* @param \IPS\Member $paidTo Revoke the commission from this member
|
||||
* @param \IPS\nexus\Money $commission Amount of commission to revoke
|
||||
* @param \IPS\nexus\Invoice\Item|null $item Item commission was for (if applicable)
|
||||
*/
|
||||
protected function revokeCommission( \IPS\Member $paidTo, \IPS\nexus\Money $commission, \IPS\nexus\Invoice\Item $item=NULL )
|
||||
{
|
||||
$credits = $paidTo->cm_credits;
|
||||
$credits[ $this->currency ]->amount = $credits[ $this->currency ]->amount->subtract( $commission->amount );
|
||||
$paidTo->cm_credits = $credits;
|
||||
$paidTo->save();
|
||||
|
||||
$paidTo->log( 'comission', array(
|
||||
'type' => 'invoice_refund',
|
||||
'currency' => $this->currency,
|
||||
'amount' => $commission->amount,
|
||||
'new_amount' => $credits[ $this->currency ]->amount,
|
||||
'member' => $this->member->member_id,
|
||||
'invoice_id' => $this->id,
|
||||
'invoice_title' => $this->title
|
||||
), FALSE );
|
||||
|
||||
\IPS\Email::buildFromTemplate( 'nexus', 'commissionRevoked', array( $this, $item, $commission ), \IPS\Email::TYPE_TRANSACTIONAL )
|
||||
->send(
|
||||
$paidTo,
|
||||
array_map(
|
||||
function( $contact )
|
||||
{
|
||||
return $contact->alt_id->email;
|
||||
},
|
||||
iterator_to_array( $paidTo->alternativeContacts( array( 'billing=1' ) ) )
|
||||
),
|
||||
( ( \in_array( 'commission_earned', explode( ',', \IPS\Settings::i()->nexus_notify_copy_types ) ) AND \IPS\Settings::i()->nexus_notify_copy_email ) ? explode( ',', \IPS\Settings::i()->nexus_notify_copy_email ) : array() )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* What will happen if we mark this unpaid?
|
||||
@@ -2243,7 +2260,26 @@ class _Invoice extends \IPS\Patterns\ActiveRecord
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Referral Commission */
|
||||
$extra = $this->status_extra;
|
||||
if( !empty( $extra['commissionref'] ) AND !empty( $extra['commissionamount'] ) )
|
||||
{
|
||||
$customer = \IPS\nexus\Customer::load( $extra['commissionref'] );
|
||||
$amount = new \IPS\nexus\Money( $extra['commissionamount'], $this->currency );
|
||||
$message = \IPS\Member::loggedIn()->language()->addToStack( 'account_credit_remove_commission', FALSE, array( 'sprintf' => array( $amount, $customer->cm_name ) ) );
|
||||
|
||||
$credits = $customer->cm_credits;
|
||||
if ( !$credits[ $amount->currency ]->amount->subtract( $amount->amount )->isPositive() )
|
||||
{
|
||||
$return[] = array( 'message' => $message, 'warning' => \IPS\Member::loggedIn()->language()->addToStack( 'account_credit_remove_neg' ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
$return[] = $message;
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
|
||||
@@ -372,7 +372,7 @@ abstract class _Item
|
||||
{
|
||||
/* If one is set to be associated with something in the cart and the other is set to be associated
|
||||
with an existing purchase, trying to compare those will throw an error */
|
||||
if ( $item->parent or $this->parent )
|
||||
if ( isset( $item->parent ) and isset( $this->parent ) )
|
||||
{
|
||||
if ( \gettype( $this->parent ) != \gettype( $item->parent ) )
|
||||
{
|
||||
|
||||
@@ -120,7 +120,6 @@ class _CustomField extends \IPS\CustomField
|
||||
'Radio' => array( 'cf_sticky' ),
|
||||
'Text' => array( 'cf_sticky' ),
|
||||
'Url' => array( 'cf_sticky' ),
|
||||
'YesNo' => array( 'cf_sticky' ),
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -189,6 +188,12 @@ class _CustomField extends \IPS\CustomField
|
||||
$values['pf_multiple'] = 0;
|
||||
}
|
||||
|
||||
/* Disable 'sticky' for fields that don't support it */
|
||||
if( !\in_array( 'cf_sticky', static::$additionalFieldToggles[ $values['pf_type'] ] ?? [] ) )
|
||||
{
|
||||
$values['cf_sticky'] = false;
|
||||
}
|
||||
|
||||
return parent::formatFormValues( $values );
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ class _Filter extends \IPS\Node\Model
|
||||
* @brief [Node] Description suffix. If specified, will look for a language key with "{$titleLangPrefix}_{$id}_{$descriptionLangSuffix}" as the key
|
||||
*/
|
||||
public static $descriptionLangSuffix = '_public';
|
||||
|
||||
|
||||
/**
|
||||
* @brief [Node] ACP Restrictions
|
||||
* @code
|
||||
@@ -192,6 +192,8 @@ class _Filter extends \IPS\Node\Model
|
||||
public function delete()
|
||||
{
|
||||
\IPS\Db::i()->delete( 'nexus_package_filters_values', array( 'pfv_filter=?', $this->id ) );
|
||||
\IPS\Lang::deleteCustom( 'nexus', static::$titleLangPrefix . $this->_id );
|
||||
\IPS\Lang::deleteCustom( 'nexus', static::$titleLangPrefix . $this->_id . static::$descriptionLangSuffix );
|
||||
parent::delete();
|
||||
}
|
||||
|
||||
|
||||
@@ -583,7 +583,7 @@ class _Item extends \IPS\Content\Item implements \IPS\Content\Featurable, \IPS\C
|
||||
*/
|
||||
public function metaDescription( $return = NULL )
|
||||
{
|
||||
return \IPS\Member::loggedIn()->language()->addToStack("nexus_package_{$this->id}_desc", FALSE, array( 'striptags' => TRUE ) );
|
||||
return \IPS\Member::loggedIn()->language()->addToStack("nexus_package_{$this->id}_desc", FALSE, array( 'striptags' => TRUE, 'removeNewlines' => TRUE ) );
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,6 +27,12 @@ class _Package extends \IPS\Node\Model
|
||||
* @brief [ActiveRecord] Multiton Store
|
||||
*/
|
||||
protected static $multitons;
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Caches
|
||||
* @note Defined cache keys will be cleared automatically as needed
|
||||
*/
|
||||
protected $caches = array( 'nexus_reg_product_count' );
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database Table
|
||||
@@ -720,7 +726,16 @@ class _Package extends \IPS\Node\Model
|
||||
$priceMayChange = FALSE;
|
||||
$priceNotIncludingDiscounts = NULL;
|
||||
$relevantRenewalTerm = NULL;
|
||||
$price = static::lowestPriceFromData( $customer, $currency, $packageId, $prices, $discounts, $renewOptions, $stock, $tax, $priceMayChange, $priceNotIncludingDiscounts, $relevantRenewalTerm );
|
||||
|
||||
try
|
||||
{
|
||||
$price = static::lowestPriceFromData( $customer, $currency, $packageId, $prices, $discounts, $renewOptions, $stock, $tax, $priceMayChange, $priceNotIncludingDiscounts, $relevantRenewalTerm );
|
||||
}
|
||||
catch ( \OutOfBoundsException $e )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
$primaryPrice = new \IPS\nexus\Money( $price, $currency );
|
||||
|
||||
/* Renewals */
|
||||
@@ -1108,7 +1123,7 @@ class _Package extends \IPS\Node\Model
|
||||
$form->addHeader('package_registration');
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'p_reg', $this->reg, FALSE, array(), function( $val )
|
||||
{
|
||||
if ( $val and ( !\IPS\Request::i()->p_store_checkbox or ( \IPS\Request::i()->p_member_groups and ( !\in_array( \IPS\Settings::i()->guest_group, \IPS\Request::i()->p_member_groups ) and \IPS\Request::i()->p_member_groups_unlimited != "*" ) ) ) )
|
||||
if ( $val and ( !\IPS\Request::i()->p_store_checkbox or ( \IPS\Request::i()->p_member_groups and !\in_array( \IPS\Settings::i()->guest_group, array_keys( \IPS\Request::i()->p_member_groups ) ) ) ) )
|
||||
{
|
||||
throw new \DomainException( \IPS\Member::loggedIn()->language()->addToStack( 'p_reg_err', FALSE, array( 'sprintf' => array( \IPS\Member\Group::load( \IPS\Settings::i()->guest_group )->name ) ) ) );
|
||||
}
|
||||
@@ -2076,6 +2091,11 @@ class _Package extends \IPS\Node\Model
|
||||
}
|
||||
return $optionValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Temporarily store stock data based on query.
|
||||
*/
|
||||
protected $_stockQueryCache = [];
|
||||
|
||||
/**
|
||||
* Stock level
|
||||
@@ -2089,14 +2109,19 @@ class _Package extends \IPS\Node\Model
|
||||
case -1: // Unlimited
|
||||
return NULL;
|
||||
case -2: // Variable
|
||||
if ( \IPS\Db::i()->select( 'COUNT(*)', 'nexus_product_options', array( 'opt_package=? AND opt_stock<>0', $this->id ) )->first() )
|
||||
if( !array_key_exists( $this->id, $this->_stockQueryCache ) )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
if ( \IPS\Db::i()->select( 'COUNT(*)', 'nexus_product_options', array( 'opt_package=? AND opt_stock<>0', $this->id ) )->first() )
|
||||
{
|
||||
$this->_stockQueryCache[ $this->id ] = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->_stockQueryCache[ $this->id ] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->_stockQueryCache[ $this->id ];
|
||||
default:
|
||||
return (int) $this->stock;
|
||||
}
|
||||
@@ -2995,7 +3020,7 @@ class _Package extends \IPS\Node\Model
|
||||
{
|
||||
if ( $this->$typeKey == 'wysiwyg' )
|
||||
{
|
||||
$email = \IPS\Email::buildFromContent( $purchase->member->language()->get( "nexus_package_{$this->id}_email_{$type}_subject" ), \IPS\Text\Parser::removeLazyLoad( $this->$valueKey ), NULL, \IPS\Email::TYPE_TRANSACTIONAL, TRUE, 'package_customemail' );
|
||||
$email = \IPS\Email::buildFromContent( $purchase->member->language()->get( "nexus_package_{$this->id}_email_{$type}_subject" ), \IPS\Text\Parser::removeLazyLoad( $this->$valueKey ), NULL, \IPS\Email::TYPE_TRANSACTIONAL, \IPS\Email::WRAPPER_USE, 'package_customemail' );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3003,7 +3028,7 @@ class _Package extends \IPS\Node\Model
|
||||
\IPS\Theme::makeProcessFunction( $this->$valueKey, $functionName, '$purchase' );
|
||||
|
||||
$themeFunction = 'IPS\\Theme\\'.$functionName;
|
||||
$email = \IPS\Email::buildFromContent( $purchase->member->language()->get( "nexus_package_{$this->id}_email_{$type}_subject" ), $themeFunction( $purchase ), NULL, \IPS\Email::TYPE_TRANSACTIONAL, $this->$typeKey == 'html_template', 'package_customemail' );
|
||||
$email = \IPS\Email::buildFromContent( $purchase->member->language()->get( "nexus_package_{$this->id}_email_{$type}_subject" ), $themeFunction( $purchase ), NULL, \IPS\Email::TYPE_TRANSACTIONAL, ( $this->$typeKey == 'html_template' ? \IPS\Email::WRAPPER_USE : \IPS\Email::WRAPPER_NONE ), 'package_customemail' );
|
||||
}
|
||||
$email->send( $purchase->member );
|
||||
|
||||
@@ -3101,6 +3126,12 @@ class _Package extends \IPS\Node\Model
|
||||
/* Upgrade */
|
||||
if ( $price >= $thisPrice and ( $this->allow_upgrading or $overrideRestrictions ) )
|
||||
{
|
||||
/* We want to pro-rata renewals for upgrades, but we do not have any renewals here, so no */
|
||||
if ( $package->upgrade_charge == 2 and ! $purchase->renewals )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$return[] = $package;
|
||||
$includesUpgrades = TRUE;
|
||||
}
|
||||
@@ -3420,11 +3451,11 @@ class _Package extends \IPS\Node\Model
|
||||
$renewalOptions = json_decode( $newPackage->renew_options, TRUE );
|
||||
if ( $chosenRenewalOption === NULL )
|
||||
{
|
||||
if ( \count( $renewalOptions ) === 1 )
|
||||
if ( $renewalOptions AND \count( $renewalOptions ) === 1 )
|
||||
{
|
||||
$term = array_pop( $renewalOptions );
|
||||
}
|
||||
elseif ( \count( $renewalOptions ) !== 0 )
|
||||
elseif ( $renewalOptions AND \count( $renewalOptions ) !== 0 )
|
||||
{
|
||||
throw new \InvalidArgumentException;
|
||||
}
|
||||
@@ -3489,6 +3520,19 @@ class _Package extends \IPS\Node\Model
|
||||
{
|
||||
$purchase->expire = NULL;
|
||||
}
|
||||
|
||||
/* Initial Term, apply the difference if the new package has a longer initial term */
|
||||
if( $term and $newPackage->initial_term )
|
||||
{
|
||||
$initial = new \DateInterval( "P{$newPackage->initial_term}" );
|
||||
|
||||
/* 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 )
|
||||
{
|
||||
$purchase->expire = $purchase->start->add( $diff );
|
||||
}
|
||||
}
|
||||
|
||||
/* Update Purchase */
|
||||
$purchase->name = \IPS\Member::loggedIn()->language()->get( "nexus_package_{$newPackage->id}" );
|
||||
@@ -3813,4 +3857,23 @@ class _Package extends \IPS\Node\Model
|
||||
{
|
||||
return \IPS\Db::i()->select( 'COUNT(*)', 'nexus_packages', 'p_reg=1 AND p_store=1' )->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* [Node] Does the currently logged in user have permission to delete this node?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function canDelete()
|
||||
{
|
||||
foreach ( \IPS\Db::i()->select( 'data', 'core_queue', array( 'app=? AND `key`=?', 'nexus', 'MassChangePurchases' ) ) as $row )
|
||||
{
|
||||
$data = json_decode( $row, TRUE );
|
||||
if ( $data['id'] == $this->_id OR $data['mass_change_purchases_to'] == $this->_id )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return parent::canDelete();
|
||||
}
|
||||
}
|
||||
@@ -143,7 +143,7 @@ class _Purchase extends \IPS\Node\Model
|
||||
$renewals = (string) $this->renewals;
|
||||
if ( $this->renewals->tax )
|
||||
{
|
||||
$renewals .= \IPS\Member::loggedIn()->language()->addToStack( 'plus_tax_rate', NULL, array( 'sprintf' => array( $this->renewals->tax->_title ) ) );
|
||||
$renewals .= \IPS\Member::loggedIn()->language()->addToStack( 'plus_tax_rate', FALSE, array( 'sprintf' => array( $this->renewals->tax->_title ) ) );
|
||||
}
|
||||
$description[] = $renewals;
|
||||
}
|
||||
@@ -1481,11 +1481,13 @@ class _Purchase extends \IPS\Node\Model
|
||||
/* !ACP Display */
|
||||
|
||||
/**
|
||||
* ACP URL
|
||||
* Get the URL of the AdminCP page for this node
|
||||
*
|
||||
* @return \IPS\Http\Url
|
||||
* @param string|NULL $do The "do" query parameter of the url (e.g. 'form', 'permissions', etc).
|
||||
*
|
||||
* @return \IPS\Http\Url | NULL
|
||||
*/
|
||||
public function acpUrl()
|
||||
public function acpUrl( $do="form" )
|
||||
{
|
||||
return \IPS\Http\Url::internal( "app=nexus&module=customers&controller=purchases&do=view&id={$this->id}", 'admin' );
|
||||
}
|
||||
@@ -1512,6 +1514,7 @@ class _Purchase extends \IPS\Node\Model
|
||||
);
|
||||
}
|
||||
|
||||
$extension = NULL;
|
||||
try
|
||||
{
|
||||
$extension = $this->extension();
|
||||
@@ -1565,10 +1568,9 @@ class _Purchase extends \IPS\Node\Model
|
||||
|
||||
if ( ( !$this->billing_agreement or $this->billing_agreement->canceled ) and ( !$this->grouped_renewals or !$parent or !$parent->billing_agreement or $parent->billing_agreement->canceled ) and \IPS\Member::loggedIn()->hasAcpRestriction( 'nexus', 'customers', 'purchases_cancel' ) )
|
||||
{
|
||||
$extension = $this->extension();
|
||||
if ( $this->cancelled )
|
||||
{
|
||||
if ( $extension::canAcpReactivate( $this ) )
|
||||
if ( $extension AND $extension::canAcpReactivate( $this ) )
|
||||
{
|
||||
$return['reactivate'] = array(
|
||||
'icon' => 'check',
|
||||
|
||||
@@ -118,24 +118,11 @@ class _RenewalTerm
|
||||
/**
|
||||
* Number of days
|
||||
*
|
||||
* @return int
|
||||
* @return string
|
||||
*/
|
||||
public function days()
|
||||
public function days(): string
|
||||
{
|
||||
$days = 0;
|
||||
if ( $this->interval->y )
|
||||
{
|
||||
$days += ( 365 * $this->interval->y );
|
||||
}
|
||||
if ( $this->interval->m )
|
||||
{
|
||||
$days += ( ( 365 / 12 ) * $this->interval->m );
|
||||
}
|
||||
if ( $this->interval->d )
|
||||
{
|
||||
$days += $this->interval->d;
|
||||
}
|
||||
return number_format( $days, 2, '.', '' );
|
||||
return number_format( \IPS\DateTime::intervalToDays( $this->interval ), 2, '.', '' );
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,6 +29,12 @@ class _Package extends \IPS\Node\Model
|
||||
* @brief [ActiveRecord] Multiton Store
|
||||
*/
|
||||
protected static $multitons;
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Caches
|
||||
* @note Defined cache keys will be cleared automatically as needed
|
||||
*/
|
||||
protected $caches = array( 'nexus_sub_count' );
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database Table
|
||||
@@ -602,15 +608,15 @@ class _Package extends \IPS\Node\Model
|
||||
{
|
||||
/* Fetch purchase */
|
||||
$purchase = NULL;
|
||||
foreach ( \IPS\nexus\extensions\nexus\Item\Subscription::getPurchases( $member, $package->id ) as $row )
|
||||
foreach ( \IPS\nexus\extensions\nexus\Item\Subscription::getPurchases( $member, $package->id, TRUE, TRUE ) as $row )
|
||||
{
|
||||
if ( $row->active and ! $row->cancelled )
|
||||
if ( !$row->cancelled OR ( $row->cancelled AND $row->can_reactivate ) )
|
||||
{
|
||||
$purchase = $row;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ( $purchase === NULL )
|
||||
{
|
||||
return NULL;
|
||||
@@ -624,14 +630,13 @@ class _Package extends \IPS\Node\Model
|
||||
{
|
||||
$currency = $purchase->member->defaultCurrency();
|
||||
}
|
||||
|
||||
|
||||
$priceOfExistingPackage = json_decode( $package->price, TRUE );
|
||||
if ( isset( $priceOfExistingPackage['cost'] ) )
|
||||
{
|
||||
$priceOfExistingPackage = $priceOfExistingPackage['cost'];
|
||||
}
|
||||
$priceOfExistingPackage = $priceOfExistingPackage[ $currency ]['amount'];
|
||||
$renewalOptionsOnOldPackage = json_decode( $package->renew_options, TRUE );
|
||||
|
||||
$priceOfThisPackage = json_decode( $this->price, TRUE );
|
||||
if ( isset( $priceOfThisPackage['cost'] ) )
|
||||
@@ -640,7 +645,6 @@ class _Package extends \IPS\Node\Model
|
||||
}
|
||||
$priceOfThisPackage = $priceOfThisPackage[ $currency ]['amount'];
|
||||
$renewalOptionsOnNewPackage = json_decode( $this->renew_options, TRUE );
|
||||
|
||||
|
||||
/* It's a non-recurring subscription */
|
||||
if ( empty( $renewalOptionsOnNewPackage ) )
|
||||
@@ -677,20 +681,37 @@ class _Package extends \IPS\Node\Model
|
||||
return new \IPS\nexus\Money( 0, $currency );
|
||||
|
||||
case 1:
|
||||
/* If the purchase is expired, charge the full amount */
|
||||
if( !$purchase->active )
|
||||
{
|
||||
$newPrice = new \IPS\nexus\Money( $priceOfThisPackage, $currency );
|
||||
|
||||
/* If the package has a free trial, charge the renewal fee instead to stop endless free trials */
|
||||
if( $newPrice->amount->isZero() )
|
||||
{
|
||||
return new \IPS\nexus\Money( $renewalOptionsOnNewPackage['cost'][ $currency ]['amount'], $currency );
|
||||
}
|
||||
|
||||
return new \IPS\nexus\Money( $priceOfThisPackage, $currency );
|
||||
}
|
||||
|
||||
return new \IPS\nexus\Money( $priceOfThisPackage - $priceOfExistingPackage, $currency );
|
||||
|
||||
case 2:
|
||||
|
||||
if ( !$purchase->renewals )
|
||||
{
|
||||
return new \IPS\nexus\Money( 0, $currency );
|
||||
}
|
||||
|
||||
if ( !$renewalOptionsOnNewPackage )
|
||||
{
|
||||
throw new \InvalidArgumentException;
|
||||
}
|
||||
|
||||
/* What is the closest renewal option on the new package? We'll use that one */
|
||||
$renewalOptionsInDays = array();
|
||||
$term = ( new \IPS\nexus\Purchase\RenewalTerm( new \IPS\nexus\Money( $renewalOptionsOnNewPackage['cost'][ $currency ]['amount'], $currency ), new \DateInterval( 'P' . $renewalOptionsOnNewPackage['term'] . mb_strtoupper( $renewalOptionsOnNewPackage['unit'] ) ), $purchase->renewals->tax ) );
|
||||
$renewalOptionsInDays[ $term->days() ] = $term;
|
||||
|
||||
|
||||
$closestRenewalOption = null;
|
||||
$numberOfDaysInCurrentRenewalTerm = $purchase->renewals->days();
|
||||
foreach ( $renewalOptionsInDays as $days => $term )
|
||||
@@ -701,23 +722,64 @@ class _Package extends \IPS\Node\Model
|
||||
}
|
||||
}
|
||||
$renewalTermToUse = $renewalOptionsInDays[ $closestRenewalOption ];
|
||||
|
||||
/* What is the difference between our current renewal term and the renewal term we're moving to? */
|
||||
$diff = $purchase->renewals->diff( $renewalTermToUse );
|
||||
|
||||
/* Multiply that by how many days are left */
|
||||
$numberOfDaysInCurrentRenewalTerm = new \IPS\Math\Number( $numberOfDaysInCurrentRenewalTerm );
|
||||
$daysLeftUntilExpiry = new \IPS\Math\Number( (string) ceil( ( $purchase->expire->getTimestamp() - time() ) / 86400 ) );
|
||||
if ( $numberOfDaysInCurrentRenewalTerm->compare( $daysLeftUntilExpiry ) === 0 )
|
||||
|
||||
|
||||
/* If the purchase is not active, it is the full cost */
|
||||
if( !$purchase->active )
|
||||
{
|
||||
return $diff;
|
||||
return new \IPS\nexus\Money( $renewalTermToUse->cost->amount, $currency );
|
||||
}
|
||||
|
||||
/* Current Period Start Date */
|
||||
$startOfCurrentPeriod = $purchase->expire->sub( $purchase->renewals->interval );
|
||||
|
||||
/* DateInterval of time between subscription start and now */
|
||||
$timeSinceStartOfCurrentPeriod = $startOfCurrentPeriod->diff( \IPS\DateTime::create() );
|
||||
$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 ) ) );
|
||||
|
||||
/* Currency Worth of value used in current sub */
|
||||
$valueUsedOfSubscription = $purchase->renewals->cost->amount->divide( $numberOfDaysInCurrentRenewalTerm )->multiply( $daysRemainingInPeriod );
|
||||
|
||||
if ( !$daysRemainingInPeriod->isGreaterThanZero() )
|
||||
{
|
||||
return new \IPS\nexus\Money( new \IPS\Math\Number('0'), $currency );
|
||||
}
|
||||
else
|
||||
{
|
||||
return new \IPS\nexus\Money( $diff->amount->divide( $numberOfDaysInCurrentRenewalTerm )->multiply( $daysLeftUntilExpiry ), $currency );
|
||||
return new \IPS\nexus\Money( $renewalTermToUse->cost->amount->subtract( $valueUsedOfSubscription ), $currency );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cost to upgrade to this package (may return negative value for refund)
|
||||
*
|
||||
* @param \IPS\nexus\Subscription\Package $package The currently subscribed package
|
||||
* @param \IPS\Member $member The member.
|
||||
* @return \IPS\nexus\Money|NULL
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function costToUpgradeIncludingTax( \IPS\nexus\Subscription\Package $package, \IPS\Member $member )
|
||||
{
|
||||
$upgradeCost = $this->costToUpgrade( $package, $member );
|
||||
|
||||
if( $upgradeCost !== NULL AND $upgradeCost->amount->isGreaterThanZero() AND \IPS\Settings::i()->nexus_show_tax and $this->tax )
|
||||
{
|
||||
try
|
||||
{
|
||||
$taxRate = new \IPS\Math\Number( \IPS\nexus\Tax::load( $this->tax )->rate( \IPS\nexus\Customer::loggedIn()->estimatedLocation() ) );
|
||||
|
||||
$upgradeCost->amount = $upgradeCost->amount->add( $upgradeCost->amount->multiply( $taxRate ) );
|
||||
}
|
||||
catch ( \OutOfRangeException $e ) { }
|
||||
}
|
||||
|
||||
return $upgradeCost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the upgrade/downgrade invoice or refund
|
||||
@@ -787,7 +849,15 @@ class _Package extends \IPS\Node\Model
|
||||
{
|
||||
$currency = $purchase->member->defaultCurrency();
|
||||
}
|
||||
$term = new \IPS\nexus\Purchase\RenewalTerm( new \IPS\nexus\Money( $term['cost'][$currency]['amount'], $currency ), new \DateInterval( 'P' . $term['term'] . mb_strtoupper( $term['unit'] ) ), $newPackage->tax ? \IPS\nexus\Tax::load( $newPackage->tax ) : NULL );
|
||||
|
||||
/* Check Tax exists */
|
||||
$tax = NULL;
|
||||
try
|
||||
{
|
||||
$tax = $newPackage->tax ? \IPS\nexus\Tax::load( $newPackage->tax ) : NULL;
|
||||
}
|
||||
catch( \OutOfRangeException $e ) {}
|
||||
$term = new \IPS\nexus\Purchase\RenewalTerm( new \IPS\nexus\Money( $term['cost'][$currency]['amount'], $currency ), new \DateInterval( 'P' . $term['term'] . mb_strtoupper( $term['unit'] ) ), $tax );
|
||||
}
|
||||
|
||||
/* Remove usergroups */
|
||||
@@ -806,24 +876,39 @@ class _Package extends \IPS\Node\Model
|
||||
/* We have a term, but the unit is different from the existing package */
|
||||
elseif ( $purchase->expire and $term and $oldTerm and ( $rawTerm['unit'] != $oldTerm['unit'] ) )
|
||||
{
|
||||
$difference = $purchase->expire->getTimestamp() - time();
|
||||
|
||||
if ( $difference > 0 )
|
||||
/* If there is an upgrade cost, the expire date should be from when the current billing period started. */
|
||||
if( $costToUpgrade->amount->isGreaterThanZero() AND \IPS\Settings::i()->nexus_subs_upgrade > 0 )
|
||||
{
|
||||
$newExpire = \IPS\DateTime::ts( ( time() - $difference ) );
|
||||
$newExpire = $newExpire->add( $term->interval );
|
||||
|
||||
if ( $newExpire->getTimestamp() < time() )
|
||||
{
|
||||
$newExpire = \IPS\DateTime::create()->add( $term->interval );
|
||||
}
|
||||
|
||||
$purchase->expire = $newExpire;
|
||||
$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 );
|
||||
}
|
||||
else
|
||||
{
|
||||
$difference = $purchase->expire->getTimestamp() - time();
|
||||
|
||||
if ( $difference > 0 )
|
||||
{
|
||||
$newExpire = \IPS\DateTime::ts( ( time() - $difference ) );
|
||||
$newExpire = $newExpire->add( $term->interval );
|
||||
|
||||
if ( $newExpire->getTimestamp() < time() )
|
||||
{
|
||||
$newExpire = \IPS\DateTime::create()->add( $term->interval );
|
||||
}
|
||||
|
||||
$purchase->expire = $newExpire;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Purchase expired in the past, so it needs one renewal term adding */
|
||||
elseif( $purchase->expire->getTimestamp() < time() AND $term )
|
||||
{
|
||||
$purchase->expire = \IPS\DateTime::create()->add( $term->interval );
|
||||
}
|
||||
|
||||
/* Update Purchase */
|
||||
$purchase->name = \IPS\Member::loggedIn()->language()->get( "nexus_subs_{$newPackage->id}" );
|
||||
$purchase->cancelled = FALSE;
|
||||
$purchase->item_id = $newPackage->id;
|
||||
$purchase->renewals = $term;
|
||||
$purchase->save();
|
||||
@@ -843,7 +928,7 @@ class _Package extends \IPS\Node\Model
|
||||
/* Change the subscription itself */
|
||||
try
|
||||
{
|
||||
\IPS\nexus\Subscription::loadActiveByMember( $purchase->member )->changePackage( $newPackage, $purchase->expire );
|
||||
\IPS\nexus\Subscription::load( $purchase->id, 'sub_purchase_id' )->changePackage( $newPackage, $purchase->expire );
|
||||
}
|
||||
catch( \Exception $e )
|
||||
{
|
||||
@@ -896,10 +981,12 @@ class _Package extends \IPS\Node\Model
|
||||
/**
|
||||
* Adds a member to the subscription package
|
||||
*
|
||||
* @param \IPS\nexus\Customer $member The cutomer innit
|
||||
* @return \IPS\nexus\Subscription The new subscription object added
|
||||
* @param \IPS\nexus\Customer $member The customer innit
|
||||
* @param bool $generatePurchase Generate purchase when adding member (for manual subscriptions)
|
||||
* @param bool $purchaseRenews Does the purchase renew, or is it forever
|
||||
* @return \IPS\nexus\Subscription The new subscription object added
|
||||
*/
|
||||
public function addMember( $member )
|
||||
public function addMember( \IPS\nexus\Customer $member, bool $generatePurchase=FALSE, bool $purchaseRenews=FALSE ): \IPS\nexus\Subscription
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -929,6 +1016,39 @@ class _Package extends \IPS\Node\Model
|
||||
$sub->expire = $expires;
|
||||
$sub->renews = $renews;
|
||||
$sub->save();
|
||||
|
||||
/* Do we need to generate a purchase to track this sub? */
|
||||
if( $generatePurchase )
|
||||
{
|
||||
$purchase = new \IPS\nexus\Purchase;
|
||||
$purchase->member = $member;
|
||||
$purchase->name = $member->language()->get( static::$titleLangPrefix . $this->id );
|
||||
$purchase->app = 'nexus';
|
||||
$purchase->type = 'subscription';
|
||||
$purchase->item_id = $this->id;
|
||||
$purchase->tax = $this->tax;
|
||||
$purchase->show = 1;
|
||||
|
||||
if( $purchaseRenews )
|
||||
{
|
||||
if ( $renewalTerm = $this->renewalTerm() )
|
||||
{
|
||||
$purchase->renewals = $renewalTerm;
|
||||
}
|
||||
$purchase->expire = \IPS\DateTime::ts( $expires );
|
||||
}
|
||||
else
|
||||
{
|
||||
$sub->expire = 0;
|
||||
$sub->renews = 0;
|
||||
}
|
||||
|
||||
$purchase->save();
|
||||
|
||||
/* update sub with purchase id */
|
||||
$sub->purchase_id = $purchase->id;
|
||||
$sub->save();
|
||||
}
|
||||
|
||||
$this->_addUsergroups( $member );
|
||||
|
||||
@@ -956,7 +1076,7 @@ class _Package extends \IPS\Node\Model
|
||||
* @param \IPS\nexus\Customer $member The cutomer innit
|
||||
* @return void
|
||||
*/
|
||||
public function cancelMember( $member)
|
||||
public function cancelMember( $member )
|
||||
{
|
||||
/* Run before marking it inactive or it won't find the row in _removeUsergroups */
|
||||
$this->_removeUsergroups( $member );
|
||||
@@ -981,7 +1101,7 @@ class _Package extends \IPS\Node\Model
|
||||
* @param \IPS\nexus\Customer $member The cutomer innit
|
||||
* @return void
|
||||
*/
|
||||
public function removeMember( $member)
|
||||
public function removeMember( $member )
|
||||
{
|
||||
/* Run before marking it inactive or it won't find the row in _removeUsergroups */
|
||||
$this->_removeUsergroups( $member );
|
||||
@@ -1013,14 +1133,23 @@ class _Package extends \IPS\Node\Model
|
||||
/* Hang on, are we about to boot someone out the ACP? */
|
||||
if ( ! ( $member->isAdmin() and !\in_array( $this->primary_group, array_keys( \IPS\Member::administrators()['g'] ) ) ) )
|
||||
{
|
||||
/* Save the current group */
|
||||
$previousGroup = $member->member_group_id;
|
||||
/* Only do this if the target group exists */
|
||||
try
|
||||
{
|
||||
$group = \IPS\Member\Group::load( $this->primary_group);
|
||||
/* Save the current group */
|
||||
$previousGroup = $member->member_group_id;
|
||||
|
||||
/* And update to the new group */
|
||||
$member->member_group_id = $this->primary_group;
|
||||
$member->members_bitoptions['ignore_promotions'] = true;
|
||||
$member->save();
|
||||
$member->logHistory( 'core', 'group', array( 'type' => 'primary', 'by' => 'subscription', 'action' => 'add', 'id' => $this->id, 'old' => $previousGroup, 'new' => $member->member_group_id ) );
|
||||
/* And update to the new group */
|
||||
$member->member_group_id = $this->primary_group;
|
||||
$member->members_bitoptions['ignore_promotions'] = true;
|
||||
$member->save();
|
||||
$member->logHistory( 'core', 'group', array( 'type' => 'primary', 'by' => 'subscription', 'action' => 'add', 'id' => $this->id, 'old' => $previousGroup, 'new' => $member->member_group_id ) );
|
||||
}
|
||||
catch( \OutOfRangeException $e )
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1035,7 +1164,16 @@ class _Package extends \IPS\Node\Model
|
||||
{
|
||||
if ( !\in_array( $gid, $newSecondary ) )
|
||||
{
|
||||
$newSecondary[] = $gid;
|
||||
/* Only do this if the target group exists */
|
||||
try
|
||||
{
|
||||
$group = \IPS\Member\Group::load( $gid );
|
||||
$newSecondary[] = $gid;
|
||||
}
|
||||
catch( \OutOfRangeException $e )
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,12 @@ class _Subscription extends \IPS\Patterns\ActiveRecord
|
||||
* @brief Multiton Store
|
||||
*/
|
||||
protected static $multitons;
|
||||
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database ID Fields
|
||||
*/
|
||||
protected static $databaseIdFields = array( 'sub_purchase_id' );
|
||||
|
||||
/**
|
||||
* Get the DateTime object for when this little subscription doth run out
|
||||
*
|
||||
@@ -67,16 +72,6 @@ class _Subscription extends \IPS\Patterns\ActiveRecord
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the subscription expired
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isExpired() : bool
|
||||
{
|
||||
return $this->expire AND $this->expire < \IPS\DateTime::create()->getTimestamp();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the package (ooh, very mysterious, sounds like something from Narcos)
|
||||
*
|
||||
@@ -133,10 +128,18 @@ class _Subscription extends \IPS\Patterns\ActiveRecord
|
||||
*/
|
||||
public function currentBlurb()
|
||||
{
|
||||
if ( !$this->active )
|
||||
if ( !$this->active AND !$this->purchase->cancelled )
|
||||
{
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'nexus_subs_subscribed_expired' );
|
||||
}
|
||||
elseif( $this->purchase->cancelled AND $this->purchase->can_reactivate )
|
||||
{
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'nexus_subs_subscribed_cancelled' );
|
||||
}
|
||||
elseif( $this->purchase->cancelled )
|
||||
{
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'nexus_subs_subscribed_cancelled_no_reactivate' );
|
||||
}
|
||||
if ( $this->expire and $this->renews and !$this->manually_added )
|
||||
{
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'nexus_subs_subscribed_with_expire' . ( ( $this->purchase and $this->purchase->renewals ) ? '' : '_no_renewal' ), NULL, array( 'sprintf' => array( $this->_expire->dayAndMonth() . ' ' . $this->_expire->format('Y') ) ) );
|
||||
@@ -210,28 +213,6 @@ class _Subscription extends \IPS\Patterns\ActiveRecord
|
||||
throw new \OutOfRangeException;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a subscription by member and purchase
|
||||
*
|
||||
* @param \IPS\Member $member Take a guess
|
||||
* @param \IPS\nexus\Purchase $purchase I mean it's really writing itself
|
||||
* @return \IPS\nexus\Subscription
|
||||
* @throws \OutOfRangeException
|
||||
*/
|
||||
public static function loadByMemberAndPurchase( \IPS\Member $member, \IPS\nexus\Purchase $purchase )
|
||||
{
|
||||
try
|
||||
{
|
||||
$where = array( array( 'sub_purchase_id=? and sub_member_id=?', $purchase->id, $member->member_id ) );
|
||||
|
||||
return static::constructFromData( \IPS\Db::i()->select( '*', 'nexus_member_subscriptions', $where )->first() );
|
||||
}
|
||||
catch( \Exception $ex )
|
||||
{
|
||||
throw new \OutOfRangeException;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark all subscriptions by this member as inactive
|
||||
@@ -243,15 +224,4 @@ class _Subscription extends \IPS\Patterns\ActiveRecord
|
||||
{
|
||||
\IPS\Db::i()->update( 'nexus_member_subscriptions', array( 'sub_active' => 0 ), array( 'sub_member_id=?', $member->member_id ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Can we cancel this subscription?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function canCancel() : bool
|
||||
{
|
||||
return !$this->expire or $this->isExpired();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -126,9 +126,10 @@ class _Reply extends \IPS\Content\Comment implements \IPS\Content\Hideable
|
||||
* @param \IPS\DateTime|NULL $time The time
|
||||
* @param string|NULL $ipAddress The IP address or NULL to detect automatically
|
||||
* @param int|NULL $hiddenStatus NULL to set automatically or override: 0 = unhidden; 1 = hidden, pending moderator approval; -1 = hidden (as if hidden by a moderator)
|
||||
* @param int|NULL $anonymous NULL for no value, 0 or 1 for a value (0=no, 1=yes)
|
||||
* @return static
|
||||
*/
|
||||
public static function create( $item, $comment, $first=FALSE, $guestName=NULL, $incrementPostCount=NULL, $member=NULL, \IPS\DateTime $time=NULL, $ipAddress=NULL, $hiddenStatus=NULL )
|
||||
public static function create( $item, $comment, $first=FALSE, $guestName=NULL, $incrementPostCount=NULL, $member=NULL, \IPS\DateTime $time=NULL, $ipAddress=NULL, $hiddenStatus=NULL, $anonymous=NULL )
|
||||
{
|
||||
$obj = parent::create( $item, $comment, $first, $guestName, $incrementPostCount, $member, $time, $ipAddress, $hiddenStatus );
|
||||
|
||||
|
||||
@@ -872,6 +872,9 @@ class _Request extends \IPS\Content\Item implements \IPS\Content\ReadMarkers
|
||||
*/
|
||||
public function processAfterCreate( $comment, $values )
|
||||
{
|
||||
/* When firing a webhook immediately after creation, readers may not yet have the first reply content */
|
||||
static::$useWriteServer = TRUE;
|
||||
|
||||
parent::processAfterCreate( $comment, $values );
|
||||
|
||||
$this->afterCreateLog( $comment );
|
||||
@@ -1023,7 +1026,7 @@ class _Request extends \IPS\Content\Item implements \IPS\Content\ReadMarkers
|
||||
/* Redirect */
|
||||
if ( $goToList )
|
||||
{
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal("app=nexus&module=support&controller=requests"), \IPS\Member::loggedIn()->language()->addToStack( 'note_added_sent_to', NULL, array( 'sprintf' => $newDepartment->_title ) ) );
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal("app=nexus&module=support&controller=requests"), \IPS\Member::loggedIn()->language()->addToStack( 'note_added_sent_to', FALSE, array( 'sprintf' => $newDepartment->_title ) ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -182,7 +182,7 @@ class _StockAction extends \IPS\Node\Model
|
||||
$form->add( new \IPS\Helpers\Form\Translatable( 'action_name', NULL, TRUE, array( 'app' => 'nexus', 'key' => ( $this->id ? "nexus_stockaction_{$this->id}" : NULL ) ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Node( 'action_status', $this->status ?: 0, FALSE, array( 'class' => 'IPS\nexus\Support\Status', 'zeroVal' => 'do_not_change' ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Node( 'action_department', $this->department ?: 0, FALSE, array( 'class' => 'IPS\nexus\Support\Department', 'zeroVal' => 'do_not_move' ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'action_staff', $this->staff ? $this->staff->member_id : 0, FALSE, array( 'parse' => 'normal', 'options' => array_merge( array( 0 => \IPS\Member::loggedIn()->language()->addToStack( 'do_not_change' ) ), \IPS\nexus\Support\Request::staff() ) ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'action_staff', $this->staff ? $this->staff->member_id : 0, FALSE, array( 'parse' => 'normal', 'options' => array( 0 => \IPS\Member::loggedIn()->language()->addToStack( 'do_not_change' ) ) + \IPS\nexus\Support\Request::staff() ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'action_message_on', $this->message, FALSE, array( 'togglesOn' => array( 'action_message_editor' ) ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Editor( 'action_message', $this->message, FALSE, array( 'app' => 'nexus', 'key' => 'Support', 'autoSaveKey' => md5( 'stockaction-' . ( $this->id ?: 'new' ) ) ), NULL, NULL, NULL, 'action_message_editor' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Node( 'action_show_in', ( !$this->show_in or $this->show_in === '*' ) ? 0 : explode( ',', $this->show_in ), FALSE, array( 'class' => 'IPS\nexus\Support\Department', 'zeroVal' => 'all', 'multiple' => TRUE ) ) );
|
||||
|
||||
@@ -204,6 +204,13 @@ class _Transaction extends \IPS\Patterns\ActiveRecord
|
||||
public function set_member( \IPS\Member $member )
|
||||
{
|
||||
$this->_data['member'] = (int) $member->member_id;
|
||||
|
||||
/* If this is an incomplete member, flag them so they will not get deleted */
|
||||
if( $member->member_id AND ( empty( $member->name ) OR empty( $member->email ) OR $member->members_bitoptions['validating'] ) )
|
||||
{
|
||||
$member->members_bitoptions['created_externally'] = TRUE;
|
||||
$member->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in new issue
Block a user