Version 4.5.0

This commit is contained in:
Neo committed 2025-12-19 05:51:58 -08:00
1 parent 1a0c7fd3c2
commit f79dcf067a
3791 files changed
+248032 -76372

No files matched your search

@@ -12,7 +12,7 @@
namespace IPS\nexus\modules\admin\payments;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
@@ -22,7 +22,12 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
* Invoices
*/
class _invoices extends \IPS\Dispatcher\Controller
{
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -66,6 +71,12 @@ class _invoices extends \IPS\Dispatcher\Controller
'i_date' => \IPS\Helpers\Table\SEARCH_DATE_RANGE,
);
$table->quickSearch = 'i_id';
$table->filters = array(
'istatus_paid' => array( 'i_status=?', \IPS\nexus\Invoice::STATUS_PAID ),
'istatus_pend' => array( 'i_status=?', \IPS\nexus\Invoice::STATUS_PENDING ),
'istatus_expd' => array( 'i_status=?', \IPS\nexus\Invoice::STATUS_EXPIRED ),
'istatus_canc' => array( 'i_status=?' , \IPS\nexus\Invoice::STATUS_CANCELED )
);
$table->mainColumn = 'i_title';
if ( $customer )
{
@@ -130,7 +141,7 @@ class _invoices extends \IPS\Dispatcher\Controller
foreach ( $transactions->include as $k => $v )
{
if ( in_array( $v, array( 't_member', 't_invoice' ) ) )
if ( \in_array( $v, array( 't_member', 't_invoice' ) ) )
{
unset( $transactions->include[ $k ] );
}
@@ -205,7 +216,8 @@ class _invoices extends \IPS\Dispatcher\Controller
public function paid()
{
\IPS\Dispatcher::i()->checkAcpPermission( 'invoices_edit' );
\IPS\Session::i()->csrfCheck();
/* Load Invoice */
try
{
@@ -216,14 +228,20 @@ class _invoices extends \IPS\Dispatcher\Controller
\IPS\Output::i()->error( 'node_error', '2X190/6', 404, '' );
}
/* Do we have a billing address? */
if ( !$invoice->billaddress AND $invoice->hasItemsRequiringBillingAddress() )
{
\IPS\Output::i()->error( 'err_no_billaddress', '2X190/I', 403 );
}
/* Any pending transactions? */
if ( !isset( \IPS\Request::i()->override ) and \IPS\Member::loggedIn()->hasAcpRestriction( 'nexus', 'payments', 'transactions_manage' ) )
{
$pendingTransactions = $invoice->transactions( array( \IPS\nexus\Transaction::STATUS_WAITING, \IPS\nexus\Transaction::STATUS_HELD, \IPS\nexus\Transaction::STATUS_REVIEW, \IPS\nexus\Transaction::STATUS_GATEWAY_PENDING ) );
if ( count( $pendingTransactions ) )
if ( \count( $pendingTransactions ) )
{
$transUrl = $invoice->acpUrl();
if ( count( $pendingTransactions ) === 1 )
if ( \count( $pendingTransactions ) === 1 )
{
foreach ( $pendingTransactions as $transaction )
{
@@ -233,7 +251,7 @@ class _invoices extends \IPS\Dispatcher\Controller
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate('global', 'core')->decision( 'invoice_paid_trans', array(
'invoice_paid_trans_view' => $transUrl,
'invoice_paid_trans_ovrd' => $invoice->acpUrl()->setQueryString( array( 'do' => 'paid', 'override' => 1 ) )
'invoice_paid_trans_ovrd' => $invoice->acpUrl()->setQueryString( array( 'do' => 'paid', 'override' => 1 ) )->csrf()
) );
return;
}
@@ -248,7 +266,7 @@ class _invoices extends \IPS\Dispatcher\Controller
) );
/* Send Email */
\IPS\Email::buildFromTemplate( 'nexus', 'invoiceMarkedPaid', array( $invoice, $invoice->summary() ), \IPS\Email::TYPE_TRANSACTIONAL )
\IPS\Email::buildFromTemplate( 'nexus', 'invoiceMarkedPaid', array( $invoice, $invoice->summary( $invoice->member->language() ) ), \IPS\Email::TYPE_TRANSACTIONAL )
->send(
$invoice->member,
array_map(
@@ -258,7 +276,7 @@ class _invoices extends \IPS\Dispatcher\Controller
},
iterator_to_array( $invoice->member->alternativeContacts( array( 'billing=1' ) ) )
),
( ( in_array( 'new_invoice', 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() )
( ( \in_array( 'new_invoice', 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() )
);
/* Do it */
@@ -289,10 +307,10 @@ class _invoices extends \IPS\Dispatcher\Controller
}
/* Get gateways */
$gateways = \IPS\nexus\Gateway::manualChargeGateways();
$gateways = \IPS\nexus\Gateway::manualChargeGateways( $invoice->member );
/* Can we do this? */
if ( $invoice->status !== \IPS\nexus\Invoice::STATUS_PENDING or !count( $gateways ) )
if ( $invoice->status !== \IPS\nexus\Invoice::STATUS_PENDING or !\count( $gateways ) )
{
\IPS\Output::i()->error( 'invoice_status_err', '2X190/9', 403, '' );
}
@@ -300,7 +318,7 @@ class _invoices extends \IPS\Dispatcher\Controller
$self = $this;
/* Wizard */
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'invoice_charge_to_card' );
\IPS\Output::i()->output = new \IPS\Helpers\Wizard(
\IPS\Output::i()->output = (string) new \IPS\Helpers\Wizard(
array(
't_amount' => function( $data ) use ( $invoice )
{
@@ -322,7 +340,7 @@ class _invoices extends \IPS\Dispatcher\Controller
$paymentMethodsToggles = array();
foreach ( $gateways as $gateway )
{
foreach ( $gateway->paymentScreen( $invoice, $amountToPay, $invoice->member ) as $element )
foreach ( $gateway->paymentScreen( $invoice, $amountToPay, $invoice->member, array(), 'admin' ) as $element )
{
if ( !$element->htmlId )
{
@@ -340,7 +358,23 @@ class _invoices extends \IPS\Dispatcher\Controller
/* Build form */
$form = new \IPS\Helpers\Form( 'charge', 'invoice_charge_to_card' );
if ( count( $gateways ) > 1 )
if ( isset( \IPS\Request::i()->previousTransactions ) )
{
$form->hiddenValues['previousTransactions'] = \IPS\Request::i()->previousTransactions;
}
else
{
if ( $previousTransactions = $invoice->transactions() and \count( $previousTransactions ) )
{
$previousTransactionIds = array();
foreach ( $previousTransactions as $previousTransaction )
{
$previousTransactionIds[] = $previousTransaction->id;
}
$form->hiddenValues['previousTransactions'] = implode( ',', $previousTransactionIds );
}
}
if ( \count( $gateways ) > 1 )
{
$form->add( new \IPS\Helpers\Form\Radio( 'payment_method', NULL, TRUE, array( 'options' => $paymentMethodOptions, 'toggles' => $paymentMethodsToggles ) ) );
}
@@ -352,7 +386,7 @@ class _invoices extends \IPS\Dispatcher\Controller
/* Handle submissions */
if ( $values = $form->values() )
{
if ( count( $gateways ) === 1 )
if ( \count( $gateways ) === 1 )
{
$gateway = array_pop( $gateways );
}
@@ -372,7 +406,15 @@ class _invoices extends \IPS\Dispatcher\Controller
try
{
$transaction->auth = $gateway->auth( $transaction, $values );
$auth = $gateway->auth( $transaction, $values, NULL, array(), 'manual' );
if ( \is_array( $auth ) )
{
return $this->_webhookRedirector( $invoice, $auth );
}
else
{
$transaction->auth = $auth;
}
$transaction->capture();
$transaction->member->log( 'transaction', array(
@@ -482,6 +524,7 @@ class _invoices extends \IPS\Dispatcher\Controller
public function resend()
{
\IPS\Dispatcher::i()->checkAcpPermission( 'invoices_resend' );
\IPS\Session::i()->csrfCheck();
/* Load Invoice */
try
@@ -529,24 +572,9 @@ class _invoices extends \IPS\Dispatcher\Controller
{
\IPS\Output::i()->error( 'node_error', '2X190/D', 404, '' );
}
/* Get the address */
$address = NULL;
if ( $invoice->billaddress )
{
$address = $invoice->billaddress;
}
else
{
try
{
$address = \IPS\nexus\Customer\Address::constructFromData( \IPS\Db::i()->select( '*', 'nexus_customer_addresses', array( "member=? AND primary_billing=?", $invoice->member->member_id, 1 ) )->first() )->address;
}
catch( \UnderflowException $e ) {}
}
/* Get output */
$output = \IPS\Theme::i()->getTemplate( 'invoices', 'nexus', 'global' )->printInvoice( $invoice, $invoice->summary(), $address );
$output = \IPS\Theme::i()->getTemplate( 'invoices', 'nexus', 'global' )->printInvoice( $invoice, $invoice->summary(), $invoice->billaddress ?: $invoice->member->primaryBillingAddress() );
\IPS\Output::i()->title = 'I' . $invoice->id;
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core', 'front' )->blankTemplate( $output ) );
}
@@ -608,12 +636,12 @@ class _invoices extends \IPS\Dispatcher\Controller
/* How do we want to refund? */
$field = new \IPS\Helpers\Form\Radio( $transaction->id . '_refund_method', 'none', TRUE, array( 'options' => $refundMethods, 'toggles' => $refundMethodToggles ) );
$field->label = count( $transactions ) === 1 ? \IPS\Member::loggedIn()->language()->addToStack( 'refund_method' ) : \IPS\Member::loggedIn()->language()->addToStack( 'trans_refund_method', FALSE, array( 'sprintf' => array( $transaction->id ) ) );
$field->label = \count( $transactions ) === 1 ? \IPS\Member::loggedIn()->language()->addToStack( 'refund_method' ) : \IPS\Member::loggedIn()->language()->addToStack( 'trans_refund_method', FALSE, array( 'sprintf' => array( $transaction->id ) ) );
$form->add( $field );
if ( $refundReasons )
{
$field = new \IPS\Helpers\Form\Radio( $transaction->id . '_refund_reason', NULL, FALSE, array( 'options' => $refundReasons ), NULL, NULL, NULL, $transaction->id . '_refund_reason' );
$field->label = count( $transactions ) === 1 ? \IPS\Member::loggedIn()->language()->addToStack( 'refund_reason' ) : \IPS\Member::loggedIn()->language()->addToStack( 'trans_refund_reason', FALSE, array( 'sprintf' => array( $transaction->id ) ) );
$field->label = \count( $transactions ) === 1 ? \IPS\Member::loggedIn()->language()->addToStack( 'refund_reason' ) : \IPS\Member::loggedIn()->language()->addToStack( 'trans_refund_reason', FALSE, array( 'sprintf' => array( $transaction->id ) ) );
$form->add( $field );
}
@@ -678,7 +706,7 @@ class _invoices extends \IPS\Dispatcher\Controller
}
/* Billing Agreement? */
if ( $billingAgreement = $transaction->billing_agreement )
if ( $billingAgreement = $transaction->billing_agreement AND $billingAgreement->status() !== $billingAgreement::STATUS_CANCELED )
{
$field = new \IPS\Helpers\Form\YesNo( $transaction->id . '_refund_cancel_billing_agreement', TRUE, NULL, array( 'togglesOff' => array( "form_{$transaction->id}_refund_cancel_billing_agreement_warning" ) ) );
$field->label = \IPS\Member::loggedIn()->language()->addToStack( 'refund_cancel_billing_agreement' );
@@ -922,7 +950,7 @@ class _invoices extends \IPS\Dispatcher\Controller
/* Select Customer */
$steps = array();
if ( !isset( \IPS\Request::i()->member ) and !$invoice )
if ( !isset( \IPS\Request::i()->member ) and ( !$invoice or !$invoice->member->member_id ) )
{
$steps['invoice_generate_member'] = function( $data )
{
@@ -945,7 +973,7 @@ class _invoices extends \IPS\Dispatcher\Controller
$form->addHeader( 'invoice_settings' );
$currencies = \IPS\nexus\Money::currencies();
if ( count( $currencies ) > 1 )
if ( \count( $currencies ) > 1 )
{
$form->add( new \IPS\Helpers\Form\Radio( 'currency', $invoice ? $invoice->currency : $customer->defaultCurrency(), TRUE, array( 'options' => array_combine( $currencies, $currencies ) ) ) );
}
@@ -962,9 +990,25 @@ class _invoices extends \IPS\Dispatcher\Controller
$form->add( new \IPS\Helpers\Form\Text( 'invoice_po_number', $invoice ? $invoice->po_number : NULL, FALSE, array( 'maxLength' => 255 ) ) );
$form->add( new \IPS\Helpers\Form\TextArea( 'invoice_notes', $invoice ? $invoice->notes : NULL ) );
$needTaxStatus = NULL;
foreach ( \IPS\nexus\Tax::roots() as $tax )
{
if ( $tax->type === 'eu' )
{
$needTaxStatus = 'eu';
break;
}
if ( $tax->type === 'business' )
{
$needTaxStatus = 'business';
}
}
$addressHelperClass = $needTaxStatus ? 'IPS\nexus\Form\BusinessAddress' : 'IPS\Helpers\Form\Address';
$addressHelperOptions = ( $needTaxStatus === 'eu' ) ? array( 'vat' => TRUE ) : array();
$form->addHeader( 'invoice_generate_addresses' );
$addresses = \IPS\Db::i()->select( '*', 'nexus_customer_addresses', array( 'member=?', $customer->member_id ) );
if ( count( $addresses ) )
$addresses = \IPS\Db::i()->select( '*', 'nexus_customer_addresses', array( '`member`=?', $customer->member_id ) );
if ( \count( $addresses ) )
{
$primaryBillingAddressId = NULL;
$chosenBillingAddressId = 0;
@@ -973,7 +1017,7 @@ class _invoices extends \IPS\Dispatcher\Controller
$options = array();
foreach ( new \IPS\Patterns\ActiveRecordIterator( $addresses, 'IPS\nexus\Customer\Address' ) as $address )
{
$options[ $address->id ] = (string) $address->address;
$options[ $address->id ] = $address->address->toString('<br>') . ( ( isset( $address->address->business ) and $address->address->business and isset( $address->address->vat ) and $address->address->vat ) ? ( '<br>' . \IPS\Member::loggedIn()->language()->addToStack('cm_checkout_vat_number') . ': ' . \IPS\Theme::i()->getTemplate( 'global', 'nexus' )->vatNumber( $address->address->vat ) ) : '' );
if ( $address->primary_billing )
{
$primaryBillingAddressId = $address->id;
@@ -992,28 +1036,33 @@ class _invoices extends \IPS\Dispatcher\Controller
$chosenShippingAddressid = $address->id;
}
}
$options[0] = 'other';
$options[0] = \IPS\Member::loggedIn()->language()->addToStack('other');
$form->add( new \IPS\Helpers\Form\Radio( 'billing_address', ( $invoice and $invoice->billaddress ) ? $chosenBillingAddressId : $primaryBillingAddressId, TRUE, array( 'options' => $options, 'toggles' => array( 0 => array( 'new_billing_address' ) ) ) ) );
$newAddress = new \IPS\Helpers\Form\Address( 'new_billing_address', $invoice ? $invoice->billaddress : NULL, FALSE, array(), NULL, NULL, NULL, 'new_billing_address' );
$form->add( new \IPS\Helpers\Form\Radio( 'billing_address', ( $invoice and $invoice->billaddress ) ? $chosenBillingAddressId : $primaryBillingAddressId, TRUE, array( 'options' => $options, 'toggles' => array( 0 => array( 'new_billing_address' ) ), 'parse' => 'raw' ) ) );
$newAddress = new $addressHelperClass( 'new_billing_address', $invoice ? $invoice->billaddress : NULL, FALSE, $addressHelperOptions, NULL, NULL, NULL, 'new_billing_address' );
$newAddress->label = ' ';
$form->add( $newAddress );
$form->add( new \IPS\Helpers\Form\Radio( 'shipping_address', ( $invoice and $invoice->shipaddress ) ? $chosenShippingAddressid : ( ( $primaryShippingAddressId == $primaryBillingAddressId ) ? 'x' : $primaryShippingAddressId ), TRUE, array( 'options' => array( 'x' => 'same_as_billing_address' ) + $options, 'toggles' => array( 0 => array( 'new_shipping_address' ) ) ) ) );
$newAddress = new \IPS\Helpers\Form\Address( 'new_shipping_address', $invoice ? $invoice->shipaddress : NULL, FALSE, array(), NULL, NULL, NULL, 'new_shipping_address' );
$form->add( new \IPS\Helpers\Form\Radio( 'shipping_address', ( $invoice and $invoice->shipaddress ) ? $chosenShippingAddressid : ( ( $primaryShippingAddressId == $primaryBillingAddressId ) ? 'x' : $primaryShippingAddressId ), TRUE, array( 'options' => array( 'x' => \IPS\Member::loggedIn()->language()->addToStack('same_as_billing_address') ) + $options, 'toggles' => array( 0 => array( 'new_shipping_address' ) ), 'parse' => 'raw' ) ) );
$newAddress = new $addressHelperClass( 'new_shipping_address', $invoice ? $invoice->shipaddress : NULL, FALSE, array(), NULL, NULL, NULL, 'new_shipping_address' );
$newAddress->label = ' ';
$form->add( $newAddress );
}
else
{
$form->add( new \IPS\Helpers\Form\Address( 'new_billing_address', $invoice ? $invoice->billaddress : NULL, FALSE ) );
$form->add( new $addressHelperClass( 'new_billing_address', $invoice ? $invoice->billaddress : NULL, FALSE, $addressHelperOptions, function( $val ) {
if ( \IPS\Request::i()->invoice_status === \IPS\nexus\Invoice::STATUS_PAID and !$val )
{
throw new \DomainException('billing_address_req');
}
} ) );
$form->add( new \IPS\Helpers\Form\Radio( 'shipping_address', ( $invoice and $invoice->shipaddress and $invoice->shipaddress != $invoice->billaddress ) ? '1' : 'x', FALSE, array( 'options' => array( 'x' => 'same_as_billing_address', '1' => 'other' ), 'toggles' => array( '1' => array( 'new_shipping_address' ) ) ) ) );
$form->add( new \IPS\Helpers\Form\Address( 'new_shipping_address', $invoice ? $invoice->billaddress : NULL, FALSE, array(), NULL, NULL, NULL, 'new_shipping_address' ) );
$form->add( new $addressHelperClass( 'new_shipping_address', $invoice ? $invoice->billaddress : NULL, FALSE, array(), NULL, NULL, NULL, 'new_shipping_address' ) );
}
if ( $values = $form->values() )
{
if ( count( $addresses ) and $values['billing_address'] )
if ( \count( $addresses ) and $values['billing_address'] )
{
$data['billaddress'] = \IPS\nexus\Customer\Address::load( $values['billing_address'] )->address;
}
@@ -1029,7 +1078,7 @@ class _invoices extends \IPS\Dispatcher\Controller
}
}
if ( count( $addresses ) and $values['shipping_address'] and $values['shipping_address'] !== 'x' )
if ( \count( $addresses ) and $values['shipping_address'] and $values['shipping_address'] !== 'x' )
{
$data['shipaddress'] = \IPS\nexus\Customer\Address::load( $values['shipping_address'] )->address;
}
@@ -1120,6 +1169,21 @@ class _invoices extends \IPS\Dispatcher\Controller
$invoice->status = $data['status'];
$invoice->save();
}
/* Now that we have an ID, do we need to update purchase rows? */
if ( isset( $data['update_purchase_invoice_pending'] ) and \is_array( $data['update_purchase_invoice_pending'] ) )
{
foreach( $data['update_purchase_invoice_pending'] as $id )
{
try
{
$purchase = \IPS\nexus\Purchase::load( $id );
$purchase->invoice_pending = $invoice;
$purchase->save();
}
catch( \Exception $e ) {}
}
}
$invoice->sendNotification();
@@ -1144,6 +1208,7 @@ class _invoices extends \IPS\Dispatcher\Controller
foreach ( $values['purchases_to_renew'] as $purchase )
{
$invoice->addItem( \IPS\nexus\Invoice\Item\Renewal::create( $purchase, $values['renew_cycles'] ) );
$data['update_purchase_invoice_pending'][] = $purchase->id;
}
$data['items'] = $invoice->items->getArrayCopy();
$_SESSION[ 'wizard-' . md5( $url ) . '-data' ] = $data;
@@ -1180,7 +1245,7 @@ class _invoices extends \IPS\Dispatcher\Controller
if ( $secondStepValues = $secondStepForm->values() )
{
$item = $class::createFromForm( $secondStepValues, $invoice );
if( is_array( $item ) )
if( \is_array( $item ) )
{
foreach ( $item as $i )
{
@@ -1196,12 +1261,20 @@ class _invoices extends \IPS\Dispatcher\Controller
\IPS\Output::i()->redirect( $url );
return;
}
return $secondStepForm;
if ( \IPS\Request::i()->isAjax() )
{
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->blankTemplate( $secondStepForm ), 200, 'text/html' );
}
else
{
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->globalTemplate( \IPS\Output::i()->title, $secondStepForm, array( 'app' => \IPS\Dispatcher::i()->application->directory, 'module' => \IPS\Dispatcher::i()->module->key, 'controller' => \IPS\Dispatcher::i()->controller ) ), 200, 'text/html' );
}
}
}
$item = $class::createFromForm( $values, $invoice );
if( is_array( $item ) )
if( \is_array( $item ) )
{
foreach ( $item as $i )
{
@@ -1220,11 +1293,11 @@ class _invoices extends \IPS\Dispatcher\Controller
if ( \IPS\Request::i()->isAjax() )
{
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->blankTemplate( $form ), 200, 'text/html', \IPS\Output::i()->httpHeaders );
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->blankTemplate( $form ), 200, 'text/html' );
}
else
{
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->globalTemplate( \IPS\Output::i()->title, $form, array( 'app' => \IPS\Dispatcher::i()->application->directory, 'module' => \IPS\Dispatcher::i()->module->key, 'controller' => \IPS\Dispatcher::i()->controller ) ), 200, 'text/html', \IPS\Output::i()->httpHeaders );
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->globalTemplate( \IPS\Output::i()->title, $form, array( 'app' => \IPS\Dispatcher::i()->application->directory, 'module' => \IPS\Dispatcher::i()->module->key, 'controller' => \IPS\Dispatcher::i()->controller ) ), 200, 'text/html' );
}
return;
}
@@ -1270,13 +1343,13 @@ class _invoices extends \IPS\Dispatcher\Controller
protected function settings()
{
\IPS\Dispatcher::i()->checkAcpPermission( 'invoices_settings' );
$form = new \IPS\Helpers\Form;
$form->addheader('invoice_flow');
$form->addMessage('invoice_flow_visualise');
$form->add( new \IPS\Helpers\Form\Number( 'cm_invoice_generate', \IPS\Settings::i()->cm_invoice_generate, FALSE, array( 'min' => 1 ), NULL, NULL, \IPS\Member::loggedIn()->language()->addToStack('cm_invoice_generate_suffix') ) );
$form->add( new \IPS\Helpers\Form\Number( 'cm_invoice_warning', \IPS\Settings::i()->cm_invoice_warning, FALSE, array( 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, NULL, \IPS\Member::loggedIn()->language()->addToStack('cm_invoice_warning_suffix') ) );
$form->add( new \IPS\Helpers\Form\Number( 'cm_invoice_expireafter', \IPS\Settings::i()->cm_invoice_expireafter, FALSE, array( 'unlimited' => 0 ), NULL, NULL, \IPS\Member::loggedIn()->language()->addToStack('days') ) );
$form->add( new \IPS\Helpers\Form\Interval( 'cm_invoice_generate', \IPS\Settings::i()->cm_invoice_generate, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::HOURS, 'min' => 1 ), NULL, NULL, \IPS\Member::loggedIn()->language()->addToStack('cm_invoice_generate_suffix') ) );
$form->add( new \IPS\Helpers\Form\Interval( 'cm_invoice_warning', \IPS\Settings::i()->cm_invoice_warning, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::HOURS, 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, NULL, \IPS\Member::loggedIn()->language()->addToStack('cm_invoice_warning_suffix') ) );
$form->add( new \IPS\Helpers\Form\Interval( 'cm_invoice_expireafter', \IPS\Settings::i()->cm_invoice_expireafter, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::DAYS, 'unlimited' => 0 ), NULL, NULL, NULL ) );
$form->addHeader('invoice_layout');
$form->addMessage('invoice_layout_blurb');
$form->add( new \IPS\Helpers\Form\Editor( 'nexus_invoice_header', \IPS\Settings::i()->nexus_invoice_header, FALSE, array( 'app' => 'nexus', 'key' => 'Admin', 'autoSaveKey' => 'nexus-invoice-header', 'attachIds' => array( NULL, NULL, 'invoice-header' ), 'minimize' => 'nexus_invoice_header_placeholder' ) ) );
@@ -1332,4 +1405,74 @@ class _invoices extends \IPS\Dispatcher\Controller
\IPS\Output::i()->redirect( $invoice->acpUrl() );
}
/**
* Wait for the webhook for a transaction to come through before it has been created
*
* @return void
*/
public function webhook()
{
/* Load the invoice */
try
{
$invoice = \IPS\nexus\Invoice::load( \IPS\Request::i()->invoice );
}
catch ( \OutOfRangeException $e )
{
\IPS\Output::i()->error( 'node_error', '2X190/J', 404, '' );
}
/* Have we decided to give up waiting and just show a pending screen? */
if ( isset( \IPS\Request::i()->pending ) )
{
\IPS\Output::i()->error( 'webhook_not_received', '2X190/K', 404 );
}
/* Nope - show a redirector */
\IPS\Output::i()->output = $this->_webhookRedirector( $invoice, isset( \IPS\Request::i()->exclude ) ? explode( ',', \IPS\Request::i()->exclude ) : array() );
}
/**
* Get a redirector that points to do=webhook
*
* @param \IPS\nexus\Invoice $invoice The invoice
* @param array $exclude Transaction IDs to exclude
* @return \IPS\Helpers\MultipleRedirect
*/
protected function _webhookRedirector( \IPS\nexus\Invoice $invoice, $exclude )
{
$self = $this;
return new \IPS\Helpers\MultipleRedirect(
\IPS\Http\Url::internal('app=nexus&module=payments&controller=invoices')->setQueryString( array( 'do' => 'webhook', 'invoice' => $invoice->id, 'exclude' => implode( ',', $exclude ) ) ),
function( $data ) use ( $self, $invoice, $exclude ) {
if ( $data === NULL )
{
return array( time(), \IPS\Member::loggedIn()->language()->addToStack('processing_the_payment') );
}
else
{
/* Do we have any transactions yet? */
foreach ( $invoice->transactions( array( \IPS\nexus\Transaction::STATUS_PAID, \IPS\nexus\Transaction::STATUS_HELD, \IPS\nexus\Transaction::STATUS_REFUSED ), $exclude ? array( array( \IPS\Db::i()->in( 't_id', $exclude, TRUE ) ) ) : array() ) as $transaction )
{
$self->_redirect( $invoice );
}
$giveUpTime = ( $data + 60 );
if ( time() > $giveUpTime )
{
return NULL;
}
else
{
sleep(5);
return array( $data, \IPS\Member::loggedIn()->language()->addToStack('processing_the_payment') );
}
}
},
function() use( $invoice ) {
\IPS\Output::i()->redirect( \IPS\Http\Url::internal('app=nexus&module=payments&controller=invoices')->setQueryString( array( 'do' => 'webhook', 'invoice' => $invoice->id, 'pending' => 1 ) ) );
}
);
}
}