Version 4.7.23
This commit is contained in:
1 parent
7124a02564
commit
25ddeb65d6
1791 files changed
+76990
-44452
No files matched your search
@@ -123,11 +123,40 @@ class _CreditCard extends \IPS\Patterns\ActiveRecord
|
||||
* @param bool $admin Set to TRUE if the *admin* (opposed to the customer themselves) wants to create a new payment method
|
||||
* @param bool $showSubmitButton Will be set to a bool indicating if the submit button should be shown
|
||||
* @param array $hiddenValues Hidden values to add to the form
|
||||
* @param array|null $allowedPaymentMethods Payment method IDs that should be included. Populated when called from checkout.
|
||||
* @return array
|
||||
*/
|
||||
public static function createFormElements( \IPS\nexus\Customer $customer, $admin, &$showSubmitButton, &$hiddenValues )
|
||||
public static function createFormElements( \IPS\nexus\Customer $customer, $admin, &$showSubmitButton, &$hiddenValues, $allowedPaymentMethods=null )
|
||||
{
|
||||
$gateways = \IPS\nexus\Gateway::cardStorageGateways( $admin );
|
||||
|
||||
/* Remove any gateways that can't be used here */
|
||||
if( !empty( $allowedPaymentMethods ) )
|
||||
{
|
||||
foreach( $gateways as $id => $gateway )
|
||||
{
|
||||
if( !\in_array( $id, $allowedPaymentMethods ) )
|
||||
{
|
||||
unset( $gateways[ $id ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif( $address = $customer->primaryBillingAddress() )
|
||||
{
|
||||
/* If we're not coming from the checkout, we're trying to manually add a card.
|
||||
Check the user's country, and if it's not available, skip it */
|
||||
foreach( $gateways as $id => $gateway )
|
||||
{
|
||||
/* @var \IPS\nexus\Gateway $gateway */
|
||||
if( $gateway->countries !== '*' )
|
||||
{
|
||||
if( !\in_array( $address->country, explode( ',', $gateway->countries ) ) )
|
||||
{
|
||||
unset( $gateways[ $id ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$elements = array();
|
||||
$paymentMethodsToggles = array();
|
||||
|
||||
@@ -554,7 +554,7 @@ class _Customer extends \IPS\Member
|
||||
}
|
||||
catch ( \UnderflowException $e )
|
||||
{
|
||||
\IPS\Db::i()->insert( 'nexus_customer_spend', array( 'spend_member_id' => $this->member_id, 'spend_amount' => ( $refund ) ? $amount->multiply( new \IPS\Math\Number( "-1" ) ) : $amount, 'spend_currency' => $currency ) );
|
||||
\IPS\Db::i()->insert( 'nexus_customer_spend', array( 'spend_member_id' => $this->member_id, 'spend_amount' => ( $refund ) ? $amount->multiply( new \IPS\Math\Number( "-1" ) ) : $amount, 'spend_currency' => $currency ), TRUE );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -54,6 +54,11 @@ class _Response
|
||||
{
|
||||
return $this->data[ $key ];
|
||||
}
|
||||
/* They changed their API, this is a less fragile and backwards compatible way to do this */
|
||||
else if( $key == 'riskScore' and isset( $this->data[ 'risk_score' ] ) )
|
||||
{
|
||||
return $this->data[ 'risk_score' ];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -122,6 +122,7 @@ class _BillingAgreement extends \IPS\nexus\Customer\BillingAgreement
|
||||
/* PayPal Subscriptions */
|
||||
if ( isset( $data['plan_id'] ) )
|
||||
{
|
||||
$url = '';
|
||||
$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 )
|
||||
{
|
||||
@@ -129,7 +130,8 @@ class _BillingAgreement extends \IPS\nexus\Customer\BillingAgreement
|
||||
{
|
||||
try
|
||||
{
|
||||
\IPS\Db::i()->select( 't_id', 'nexus_transactions', array( 't_method=? AND t_gw_id=?', $this->method->id, $t['id'] ) )->first();
|
||||
/* @note SELECT_FROM_WRITE_SERVER transaction race condition */
|
||||
\IPS\Db::i()->select( 't_id', 'nexus_transactions', [ 't_gw_id=? AND t_method=?', $t['id'], $this->method->id ], flags: \IPS\Db::SELECT_FROM_WRITE_SERVER )->first();
|
||||
}
|
||||
catch ( \UnderflowException $e )
|
||||
{
|
||||
@@ -157,7 +159,8 @@ class _BillingAgreement extends \IPS\nexus\Customer\BillingAgreement
|
||||
{
|
||||
try
|
||||
{
|
||||
\IPS\Db::i()->select( 't_id', 'nexus_transactions', array( 't_method=? AND t_gw_id=?', $this->method->id, $t['transaction_id'] ) )->first();
|
||||
/* @note SELECT_FROM_WRITE_SERVER transaction race condition */
|
||||
\IPS\Db::i()->select( 't_id', 'nexus_transactions', [ 't_gw_id=? AND t_method=?', $t['transaction_id'], $this->method->id ], flags: \IPS\Db::SELECT_FROM_WRITE_SERVER )->first();
|
||||
}
|
||||
catch ( \UnderflowException $e )
|
||||
{
|
||||
|
||||
@@ -94,7 +94,7 @@ class _Exception extends \DomainException
|
||||
break;
|
||||
|
||||
default:
|
||||
if( isset( $details ) AND $details[0]['issue'] === 'INSTRUMENT_DECLINED' )
|
||||
if( isset( $details[0] ) AND $details[0]['issue'] === 'INSTRUMENT_DECLINED' )
|
||||
{
|
||||
$message = \IPS\Member::loggedIn()->language()->get( 'payment_refused' );
|
||||
}
|
||||
|
||||
@@ -623,6 +623,19 @@ class _PayPal extends \IPS\nexus\Gateway
|
||||
'return_url' => \IPS\Settings::i()->base_url . 'applications/nexus/interface/gateways/paypal.php?subscription=1&nexusTransactionId=' . $transaction->id,
|
||||
)
|
||||
) );
|
||||
|
||||
/* PayPal sometimes does not include the subscription ID in the webhook. This causes a race condition
|
||||
and we can't properly link the transaction. So let's create the Billing Agreement record now. */
|
||||
$billingAgreement = new \IPS\nexus\Gateway\PayPal\BillingAgreement;
|
||||
$billingAgreement->gw_id = $subscription['id'];
|
||||
$billingAgreement->method = $transaction->method;
|
||||
$billingAgreement->member = $transaction->member;
|
||||
$billingAgreement->started = \IPS\DateTime::create();
|
||||
$billingAgreement->next_cycle = \IPS\DateTime::create()->add( $term->interval );
|
||||
$billingAgreement->save();
|
||||
|
||||
$transaction->billing_agreement = $billingAgreement;
|
||||
$transaction->save();
|
||||
|
||||
/* Redirect */
|
||||
foreach ( $subscription['links'] as $link )
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
namespace IPS\nexus\Gateway;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
|
||||
use IPS\nexus\Transaction;
|
||||
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
@@ -41,6 +44,20 @@ class _Stripe 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 );
|
||||
|
||||
/* If we have nothing to pay now, but we have something to pay later,
|
||||
then check against the renewal amount */
|
||||
if( $amount->amount->isZero() and \count( $recurrings ) )
|
||||
{
|
||||
foreach( $recurrings as $format => $terms )
|
||||
{
|
||||
if( $terms['term'] instanceof \IPS\nexus\Purchase\RenewalTerm and $terms['term']->cost->amount->isGreaterThanZero() )
|
||||
{
|
||||
$amount = $terms['term']->cost;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Stripe has a minimum transaction fee. This is based on the businesses currency, but as we don't know what the transaction rate is
|
||||
we'll do this check only in the transactions we know - anything else will be rejected when the user tries to pay */
|
||||
@@ -301,7 +318,7 @@ class _Stripe extends \IPS\nexus\Gateway
|
||||
'data-zip' => $invoice->billaddress ? $invoice->billaddress->postalCode : NULL,
|
||||
'data-country' => $invoice->billaddress ? $invoice->billaddress->country : NULL,
|
||||
'data-email' => $member ? $member->email : $invoice->member->email,
|
||||
'data-phone' => isset( $member->cm_phone ) ? $member->cm_phone : NULL,
|
||||
'data-phone' => isset( $member->cm_phone ) ? $member->cm_phone : ( $invoice->member->cm_phone ?? null ),
|
||||
'data-amount' => static::_amountAsCents( $amount ),
|
||||
'data-currency' => $amount->currency,
|
||||
'data-setupIntent' => $setupIntent ? $setupIntent['id'] : NULL,
|
||||
@@ -314,16 +331,6 @@ class _Stripe extends \IPS\nexus\Gateway
|
||||
'member' => $member,
|
||||
) ) );
|
||||
}
|
||||
elseif ( $settings['type'] === 'sofort' and \IPS\NEXUS_TEST_GATEWAYS )
|
||||
{
|
||||
return array(
|
||||
new \IPS\Helpers\Form\Radio( 'stripe_debug_action', 'succeeding_charge', FALSE, array( 'options' => array(
|
||||
'succeeding_charge' => 'stripe_debug_succeeding_charge',
|
||||
'pending_charge' => 'stripe_debug_pending_charge',
|
||||
'failing_charge' => 'stripe_debug_failing_charge',
|
||||
) ) )
|
||||
);
|
||||
}
|
||||
elseif ( \in_array( $settings['type'], array( 'alipay', 'bancontact', 'giropay', 'ideal', 'sofort' ) ) )
|
||||
{
|
||||
return array();
|
||||
@@ -351,7 +358,7 @@ class _Stripe extends \IPS\nexus\Gateway
|
||||
* @throws
|
||||
*/
|
||||
protected function _createPaymentIntent( \IPS\nexus\Invoice $invoice, \IPS\nexus\Money $amount, \IPS\nexus\Customer $member, $paymentMethod, $savePaymentMethod = FALSE, $type = 'checkout' )
|
||||
{
|
||||
{
|
||||
$data = array(
|
||||
'amount' => static::_amountAsCents( $amount ),
|
||||
'currency' => $amount->currency,
|
||||
@@ -362,12 +369,13 @@ class _Stripe extends \IPS\nexus\Gateway
|
||||
'metadata' => array(
|
||||
"Invoice ID" => $invoice->id,
|
||||
"Customer ID" => $member->member_id,
|
||||
"Customer Email" => $member->email,
|
||||
"Customer Email" => $invoice->member->email,
|
||||
"IP Address" => \IPS\Request::i()->ipAddress(),
|
||||
"Payment Method ID" => $this->id
|
||||
),
|
||||
'payment_method' => $paymentMethod,
|
||||
'payment_method' => $paymentMethod
|
||||
);
|
||||
|
||||
if ( $type === 'admin' )
|
||||
{
|
||||
$data['metadata']["Admin"] = \IPS\Member::loggedIn()->member_id;
|
||||
@@ -425,47 +433,47 @@ class _Stripe extends \IPS\nexus\Gateway
|
||||
}
|
||||
}
|
||||
|
||||
if ( !isset( $data['customer'] ) )
|
||||
{
|
||||
if ( $member->member_id )
|
||||
{
|
||||
$response = $this->api( 'customers', array(
|
||||
'name' => $member->cm_name,
|
||||
'email' => $member->email,
|
||||
'metadata' => array(
|
||||
"Customer ID" => $member->member_id
|
||||
)
|
||||
) );
|
||||
$profiles[ $this->id ] = $response['id'];
|
||||
|
||||
$member->cm_profiles = $profiles;
|
||||
$member->save();
|
||||
}
|
||||
else
|
||||
{
|
||||
$response = $this->api( 'customers', array(
|
||||
'name' => $invoice->member->cm_name,
|
||||
'email' => $invoice->member->email,
|
||||
) );
|
||||
$profiles[ $this->id ] = $response['id'];
|
||||
|
||||
$guestData = $invoice->guest_data;
|
||||
$guestData['member']['cm_profiles'] = $profiles;
|
||||
$guestData['cards'][] = array(
|
||||
'card_method' => $this->id,
|
||||
'card_data' => $paymentMethod
|
||||
);
|
||||
$invoice->guest_data = $guestData;
|
||||
$invoice->save();
|
||||
}
|
||||
|
||||
$data['customer'] = $profiles[ $this->id ];
|
||||
}
|
||||
|
||||
if ( $savePaymentMethod )
|
||||
{
|
||||
$data['save_payment_method'] = 'true';
|
||||
$data['setup_future_usage'] = 'off_session';
|
||||
|
||||
if ( !isset( $data['customer'] ) )
|
||||
{
|
||||
if ( $member->member_id )
|
||||
{
|
||||
$response = $this->api( 'customers', array(
|
||||
'name' => $member->cm_name,
|
||||
'email' => $member->email,
|
||||
'metadata' => array(
|
||||
"Customer ID" => $member->member_id
|
||||
)
|
||||
) );
|
||||
$profiles[ $this->id ] = $response['id'];
|
||||
|
||||
$member->cm_profiles = $profiles;
|
||||
$member->save();
|
||||
}
|
||||
else
|
||||
{
|
||||
$response = $this->api( 'customers', array(
|
||||
'name' => $invoice->member->cm_name,
|
||||
'email' => $invoice->member->email,
|
||||
) );
|
||||
$profiles[ $this->id ] = $response['id'];
|
||||
|
||||
$guestData = $invoice->guest_data;
|
||||
$guestData['member']['cm_profiles'] = $profiles;
|
||||
$guestData['cards'][] = array(
|
||||
'card_method' => $this->id,
|
||||
'card_data' => $paymentMethod
|
||||
);
|
||||
$invoice->guest_data = $guestData;
|
||||
$invoice->save();
|
||||
}
|
||||
|
||||
$data['customer'] = $profiles[ $this->id ];
|
||||
}
|
||||
}
|
||||
|
||||
$response = $this->api( 'payment_intents', $data );
|
||||
@@ -481,6 +489,91 @@ class _Stripe extends \IPS\nexus\Gateway
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a payment intent for non-card payments
|
||||
*
|
||||
* @param Transaction $transaction
|
||||
* @param string|null $paymentMethod Stripe payment method ID ("pm_....") - can be for a new card or a card already on file
|
||||
* @param string $type 'checkout' means the cusotmer is doing this on the normal checkout screen, 'admin' means the admin is doing this in the ACP, 'card' means the user is just adding a card
|
||||
* @return array
|
||||
*/
|
||||
protected function _createNonCardPaymentIntent( \IPS\nexus\Transaction $transaction, ?string $paymentMethod, string $type = 'checkout' ) : array
|
||||
{
|
||||
$invoice = $transaction->invoice;
|
||||
$data = array(
|
||||
'amount' => static::_amountAsCents( $transaction->amount ),
|
||||
'currency' => $transaction->amount->currency,
|
||||
'capture_method' => 'automatic',
|
||||
'confirm' => 'true',
|
||||
'confirmation_method' => 'automatic',
|
||||
'description' => $invoice->title,
|
||||
'metadata' => array(
|
||||
"Invoice ID" => $invoice->id,
|
||||
"Customer ID" => $transaction->member->member_id,
|
||||
"Customer Email" => $transaction->invoice->member->email,
|
||||
"IP Address" => \IPS\Request::i()->ipAddress(),
|
||||
"Payment Method ID" => $this->id,
|
||||
"Transaction ID" => $transaction->id
|
||||
),
|
||||
'payment_method' => $paymentMethod,
|
||||
'payment_method_types' => [ $paymentMethod ],
|
||||
'return_url' => \IPS\Settings::i()->base_url . 'applications/nexus/interface/gateways/stripe-redirector.php?nexusTransactionId=' . $transaction->id
|
||||
);
|
||||
|
||||
if ( $type === 'admin' )
|
||||
{
|
||||
$data['metadata']["Admin"] = \IPS\Member::loggedIn()->member_id;
|
||||
}
|
||||
if ( $invoice->shipaddress )
|
||||
{
|
||||
$data['shipping'] = array(
|
||||
'address' => array(
|
||||
'city' => $invoice->shipaddress->city,
|
||||
'country' => $invoice->shipaddress->country,
|
||||
'line1' => $invoice->shipaddress->addressLines[0] ?? NULL,
|
||||
'line2' => $invoice->shipaddress->addressLines[1] ?? NULL,
|
||||
'postal_code' => $invoice->shipaddress->postalCode,
|
||||
'state' => $invoice->shipaddress->region,
|
||||
),
|
||||
'name' => $invoice->member->cm_name,
|
||||
'phone' => $invoice->member->cm_phone ?? NULL
|
||||
);
|
||||
}
|
||||
|
||||
/* Create a payment method */
|
||||
$paymentMethodData = [
|
||||
'type' => $paymentMethod,
|
||||
'billing_details' => [
|
||||
'address' => [
|
||||
'city' => $invoice->billaddress->city,
|
||||
'country' => $invoice->billaddress->country,
|
||||
'line1' => $invoice->billaddress->addressLines[0] ?? NULL,
|
||||
'line2' => $invoice->billaddress->addressLines[1] ?? NULL,
|
||||
'postal_code' => $invoice->billaddress->postalCode,
|
||||
'state' => $invoice->billaddress->region
|
||||
],
|
||||
'email' => ( $transaction->member->member_id ? $transaction->member->email : $invoice->guest_data['member']['email'] ),
|
||||
'name' => ( $transaction->member->member_id ? $transaction->member->cm_name : $invoice->guest_data['member']['cm_first_name'] . ' '. $invoice->guest_data['member']['cm_last_name'] ),
|
||||
'phone' => ( $transaction->member->member_id ? $transaction->member->cm_phone : $invoice->guest_data['member']['cm_phone'] )
|
||||
]
|
||||
];
|
||||
|
||||
if( $paymentMethod == 'sofort' )
|
||||
{
|
||||
$paymentMethodData['sofort'] = array( 'country' => $transaction->invoice->billaddress->country );
|
||||
}
|
||||
|
||||
$response = $this->api( 'payment_methods', $paymentMethodData );
|
||||
|
||||
if( isset( $response['id'] ) )
|
||||
{
|
||||
$data['payment_method'] = $response['id'];
|
||||
$data['customer'] = $response['customer'];
|
||||
}
|
||||
|
||||
return $this->api( 'payment_intents', $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Authorize
|
||||
@@ -498,50 +591,27 @@ class _Stripe extends \IPS\nexus\Gateway
|
||||
$settings = json_decode( $this->settings, TRUE );
|
||||
|
||||
/* Do we need to redirect? */
|
||||
if ( \in_array( $settings['type'], array( 'alipay', 'bancontact', 'giropay', 'ideal', 'sofort' ) ) and !isset( $values[ $this->id . '_card' ] ) )
|
||||
{
|
||||
if( \in_array( $settings['type'], [ 'alipay', 'bancontact', 'giropay', 'ideal', 'sofort' ] ) and !isset( $values[ $this->id . '_card' ] ) )
|
||||
{
|
||||
/* We need a transaction ID */
|
||||
$transaction->save();
|
||||
|
||||
/* Create the source */
|
||||
$data = array(
|
||||
'type' => $settings['type'],
|
||||
'amount' => static::_amountAsCents( $transaction->amount ),
|
||||
'currency' => $transaction->amount->currency,
|
||||
'redirect' => array(
|
||||
'return_url' => \IPS\Settings::i()->base_url . 'applications/nexus/interface/gateways/stripe-redirector.php?nexusTransactionId=' . $transaction->id
|
||||
),
|
||||
'owner' => array(
|
||||
'email' => $transaction->member->email,
|
||||
'name' => ( \IPS\NEXUS_TEST_GATEWAYS and isset( $values['stripe_debug_action'] ) ) ? $values['stripe_debug_action'] : $transaction->member->cm_name,
|
||||
)
|
||||
);
|
||||
if ( isset( $transaction->member->cm_phone ) and $transaction->member->cm_phone )
|
||||
|
||||
/* Create a payment intent */
|
||||
$response = $this->_createNonCardPaymentIntent( $transaction, $settings['type'], 'checkout' );
|
||||
if( $response['status'] == 'requires_action' )
|
||||
{
|
||||
$data['owner']['phone'] = $transaction->member->cm_phone;
|
||||
}
|
||||
if ( $settings['type'] == 'sofort' )
|
||||
{
|
||||
$data['sofort'] = array( 'country' => $transaction->invoice->billaddress->country );
|
||||
}
|
||||
if ( $transaction->invoice->billaddress )
|
||||
{
|
||||
$data['owner']['address'] = array(
|
||||
'city' => $transaction->invoice->billaddress->city,
|
||||
'country' => $transaction->invoice->billaddress->country,
|
||||
'line1' => isset( $transaction->invoice->billaddress->addressLines[0] ) ? $transaction->invoice->billaddress->addressLines[0] : NULL,
|
||||
'line2' => isset( $transaction->invoice->billaddress->addressLines[1] ) ? $transaction->invoice->billaddress->addressLines[1] : NULL,
|
||||
'postal_code' => $transaction->invoice->billaddress->postalCode,
|
||||
'state' => $transaction->invoice->billaddress->region,
|
||||
);
|
||||
}
|
||||
$response = $this->api( 'sources', $data );
|
||||
|
||||
/* Redirect the user */
|
||||
if ( isset( $response['redirect']['url'] ) )
|
||||
{
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::external( $response['redirect']['url'] ) );
|
||||
$transaction->gw_id = $response['id'];
|
||||
$transaction->status = \IPS\nexus\Transaction::STATUS_GATEWAY_PENDING;
|
||||
$transaction->save();
|
||||
|
||||
$actionType = $response['next_action']['type'];
|
||||
if ( isset( $response['next_action'][$actionType]['url'] ) )
|
||||
{
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::createFromString( $response['next_action'][$actionType]['url'] ) );
|
||||
}
|
||||
}
|
||||
|
||||
/* still here? There was an error */
|
||||
throw new \RuntimeException;
|
||||
}
|
||||
|
||||
@@ -582,7 +652,7 @@ class _Stripe extends \IPS\nexus\Gateway
|
||||
"Transaction ID" => $transaction->id,
|
||||
"Invoice ID" => $transaction->invoice->id,
|
||||
"Customer ID" => $transaction->member->member_id,
|
||||
"Customer Email" => $transaction->member->email,
|
||||
"Customer Email" => $transaction->invoice->member->email,
|
||||
),
|
||||
);
|
||||
if ( $transaction->invoice->shipaddress )
|
||||
@@ -640,6 +710,7 @@ class _Stripe extends \IPS\nexus\Gateway
|
||||
}
|
||||
elseif ( mb_substr( $values[ $this->id . '_card' ]->data, 0, 4 ) === 'src_' )
|
||||
{
|
||||
$data['capture'] = 'false'; // Don't auto-capture Apple/Google Pay.
|
||||
$data['source'] = $values[ $this->id . '_card' ]->data;
|
||||
}
|
||||
else
|
||||
@@ -838,9 +909,12 @@ class _Stripe extends \IPS\nexus\Gateway
|
||||
{
|
||||
$response = $charge;
|
||||
$response['source'] = $response['payment_method_details'];
|
||||
foreach ( array( 'cvc_check', 'address_line1_check', 'address_postal_code_check' ) as $k )
|
||||
if( isset( $response['payment_method_details']['card'] ) )
|
||||
{
|
||||
$response['source']['card'][ $k ] = isset( $response['payment_method_details']['card']['checks'][ $k ] ) ? $response['payment_method_details']['card']['checks'][ $k ] : 'unavailable';
|
||||
foreach ( array( 'cvc_check', 'address_line1_check', 'address_postal_code_check' ) as $k )
|
||||
{
|
||||
$response['source']['card'][ $k ] = isset( $response['payment_method_details']['card']['checks'][ $k ] ) ? $response['payment_method_details']['card']['checks'][ $k ] : 'unavailable';
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1010,14 +1084,16 @@ class _Stripe extends \IPS\nexus\Gateway
|
||||
|
||||
$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' ) );
|
||||
\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' ),
|
||||
\IPS\Member::loggedIn()->language()->formatList( $this->webhookEvents ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'stripe_webhook_secret', isset( $settings['webhook_secret'] ) ? $settings['webhook_secret'] : NULL, TRUE ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Webhook events we need
|
||||
*/
|
||||
protected $webhookEvents = array( 'source.chargeable', 'charge.succeeded', 'charge.failed', 'charge.dispute.created', 'charge.dispute.closed' );
|
||||
protected $webhookEvents = array( 'source.chargeable', 'charge.succeeded', 'charge.failed', 'charge.dispute.created', 'charge.dispute.closed'/*, 'payment_intent.payment_failed'*/ );
|
||||
|
||||
/**
|
||||
* Test Settings
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
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' );
|
||||
@@ -523,6 +525,7 @@ class _Invoice extends \IPS\Patterns\ActiveRecord
|
||||
public function set_guest_data( $data )
|
||||
{
|
||||
$this->_data['guest_data'] = $data ? json_encode( $data ) : NULL;
|
||||
$this->_member = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1769,10 +1772,15 @@ class _Invoice extends \IPS\Patterns\ActiveRecord
|
||||
|
||||
/* Find any billing agreements */
|
||||
$billingAgreement = NULL;
|
||||
|
||||
/* Turn off read/write separation so that we make sure to retrieve the latest records */
|
||||
$currentValue = \IPS\Db::i()->readWriteSeparation;
|
||||
\IPS\Db::i()->readWriteSeparation = false;
|
||||
foreach ( $this->transactions( array( \IPS\nexus\Transaction::STATUS_PAID, \IPS\nexus\Transaction::STATUS_PART_REFUNDED ), array( array( 't_billing_agreement IS NOT NULL' ) ) ) as $transaction )
|
||||
{
|
||||
$billingAgreement = $transaction->billing_agreement;
|
||||
}
|
||||
\IPS\Db::i()->readWriteSeparation = $currentValue;
|
||||
|
||||
/* Loop items */
|
||||
$purchases = array();
|
||||
|
||||
@@ -261,7 +261,7 @@ class _Package extends \IPS\Node\Model
|
||||
{
|
||||
if ( !\in_array( $k, array( 'id', 'reviews', 'unapproved_reviews', 'hidden_reviews' ) ) )
|
||||
{
|
||||
if ( \in_array( "p_{$k}", static::$packageDatabaseColumns ) )
|
||||
if ( isset( static::$packageDatabaseColumns ) and \in_array( "p_{$k}", static::$packageDatabaseColumns ) )
|
||||
{
|
||||
$secondaryTable[ "p_{$k}" ] = $v;
|
||||
}
|
||||
@@ -2574,10 +2574,21 @@ class _Package extends \IPS\Node\Model
|
||||
break;
|
||||
|
||||
case 'reactivate':
|
||||
if( ( $purchase->cancelled AND !$purchase->can_reactivate ) )
|
||||
{
|
||||
\IPS\Output::i()->error( 'package_cannot_reactivate_error', '2S437/1', 403, '' );
|
||||
}
|
||||
|
||||
$currency = $purchase->original_invoice->currency;
|
||||
$renewalOptions = json_decode( $this->renew_options, TRUE );
|
||||
$noOptions = \count( $renewalOptions ) === 1;
|
||||
|
||||
/* Are we able to renew this purchase? */
|
||||
if( $purchase->renewals or !\count( $renewalOptions ) or ( $purchase->billing_agreement and !$purchase->billing_agreement->canceled ) )
|
||||
{
|
||||
\IPS\Output::i()->error( 'package_cannot_reactivate_error', '2S437/2', 403, '' );
|
||||
}
|
||||
|
||||
$tax = NULL;
|
||||
if ( $purchase->tax )
|
||||
{
|
||||
|
||||
@@ -318,6 +318,18 @@ class _Purchase extends \IPS\Node\Model
|
||||
}
|
||||
|
||||
$this->_data['active'] = $active;
|
||||
|
||||
/* Set any child purchases to match this state.
|
||||
Note that this will not be executed if editing a purchase in the ACP,
|
||||
as the purchases are temporarily ungrouped at this point. */
|
||||
foreach( $this->children() as $child )
|
||||
{
|
||||
if( $child->grouped_renewals and !$child->cancelled )
|
||||
{
|
||||
$child->active = $active;
|
||||
$child->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -401,6 +413,17 @@ class _Purchase extends \IPS\Node\Model
|
||||
{
|
||||
$this->onExpirationDateChange();
|
||||
}
|
||||
|
||||
/* If we have a parent purchase and the renewals are grouped,
|
||||
set the active flag to match */
|
||||
try
|
||||
{
|
||||
if( $parent = $this->parent() and $this->grouped_renewals )
|
||||
{
|
||||
$this->active = $parent->active;
|
||||
}
|
||||
}
|
||||
catch ( \OutOfRangeException $e ){}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
namespace IPS\nexus\Subscription;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
|
||||
use IPS\DateTime;
|
||||
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
@@ -653,6 +656,13 @@ class _Package extends \IPS\Node\Model
|
||||
$priceOfThisPackage = $priceOfThisPackage[ $currency ]['amount'];
|
||||
$renewalOptionsOnNewPackage = json_decode( $this->renew_options, TRUE );
|
||||
|
||||
/* If the price of the package is greater than 0, and the renewals are 0, then this is a lifetime purchase,
|
||||
so ignore any renewals */
|
||||
if( $priceOfThisPackage > 0 and !empty( $renewalOptionsOnNewPackage ) and $renewalOptionsOnNewPackage['cost'][ $currency ]['amount'] == 0 )
|
||||
{
|
||||
$renewalOptionsOnNewPackage = null;
|
||||
}
|
||||
|
||||
/* It's a non-recurring subscription */
|
||||
if ( empty( $renewalOptionsOnNewPackage ) )
|
||||
{
|
||||
@@ -683,13 +693,13 @@ class _Package extends \IPS\Node\Model
|
||||
{
|
||||
case -1:
|
||||
return NULL; /* nope */
|
||||
|
||||
|
||||
case 0:
|
||||
return new \IPS\nexus\Money( 0, $currency );
|
||||
|
||||
|
||||
case 1:
|
||||
/* If the purchase is expired, charge the full amount */
|
||||
if( !$purchase->active )
|
||||
if( !$purchase->active or ( $purchase->expire instanceof \IPS\DateTime and $purchase->expire->getTimestamp() < time() ) )
|
||||
{
|
||||
$newPrice = new \IPS\nexus\Money( $priceOfThisPackage, $currency );
|
||||
|
||||
@@ -703,11 +713,12 @@ class _Package extends \IPS\Node\Model
|
||||
}
|
||||
|
||||
return new \IPS\nexus\Money( $priceOfThisPackage - $priceOfExistingPackage, $currency );
|
||||
|
||||
|
||||
case 2:
|
||||
if ( !$purchase->renewals )
|
||||
/* If there are no renewals, we charge full price no matter what */
|
||||
if ( !$purchase->renewals or !$purchase->expire )
|
||||
{
|
||||
return new \IPS\nexus\Money( 0, $currency );
|
||||
return new \IPS\nexus\Money( $priceOfThisPackage, $currency );
|
||||
}
|
||||
if ( !$renewalOptionsOnNewPackage )
|
||||
{
|
||||
@@ -716,7 +727,7 @@ class _Package extends \IPS\Node\Model
|
||||
|
||||
/* 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 ) );
|
||||
$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;
|
||||
@@ -729,42 +740,87 @@ class _Package extends \IPS\Node\Model
|
||||
}
|
||||
}
|
||||
$renewalTermToUse = $renewalOptionsInDays[ $closestRenewalOption ];
|
||||
|
||||
$numberOfDaysInCurrentRenewalTerm = new \IPS\Math\Number( (string) round( $numberOfDaysInCurrentRenewalTerm ) );
|
||||
|
||||
/* If the purchase is not active, it is the full cost */
|
||||
if( !$purchase->active )
|
||||
if( !$purchase->active or ( $purchase->expire instanceof \IPS\DateTime and $purchase->expire->getTimestamp() < time() ) )
|
||||
{
|
||||
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 = 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( $daysUsedInPeriod );
|
||||
|
||||
if ( !$daysRemainingInPeriod->isGreaterThanZero() )
|
||||
if( $purchase->expire instanceof \IPS\DateTime and $purchase->renewals )
|
||||
{
|
||||
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 );
|
||||
$startOfCurrentPeriod = $purchase->expire->sub( $purchase->renewals->interval );
|
||||
|
||||
/* If we renewed for multiple cycles, we might be in the future, so go back */
|
||||
$cycles = 1;
|
||||
while( $startOfCurrentPeriod->diff( \IPS\DateTime::create() )->invert )
|
||||
{
|
||||
$startOfCurrentPeriod = $startOfCurrentPeriod->sub( $purchase->renewals->interval );
|
||||
$cycles++;
|
||||
}
|
||||
|
||||
if( $cycles > 1 )
|
||||
{
|
||||
$numberOfDaysInCurrentRenewalTerm = $numberOfDaysInCurrentRenewalTerm->multiply( new \IPS\Math\Number( "{$cycles}" ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return new \IPS\nexus\Money( $renewalTermToUse->cost->amount->subtract( $valueUsedOfSubscription ), $currency );
|
||||
/* If the purchase does not expire, we have to do some fun logic here */
|
||||
$timeSincePurchase = $purchase->start->diff( \IPS\DateTime::create() );
|
||||
$daysUsedSincePurchase = new \IPS\Math\Number( (string)\IPS\DateTime::intervalToDays( $timeSincePurchase ) );
|
||||
|
||||
/* If we used less than a full renewal period, then set the start to the purchase date */
|
||||
if( $daysUsedSincePurchase->compare( $numberOfDaysInCurrentRenewalTerm ) <= 0 )
|
||||
{
|
||||
$startOfCurrentPeriod = $purchase->start;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Otherwise loop through all the used intervals */
|
||||
$start = clone $purchase->start;
|
||||
while( $daysUsedSincePurchase->compare( $numberOfDaysInCurrentRenewalTerm ) > 0 )
|
||||
{
|
||||
$start = $start->add( $purchase->renewals->interval );
|
||||
$timeSincePurchase = $start->diff( \IPS\DateTime::create() );
|
||||
$daysUsedSincePurchase = new \IPS\Math\Number( (string)\IPS\DateTime::intervalToDays( $timeSincePurchase ) );
|
||||
}
|
||||
$startOfCurrentPeriod = $start;
|
||||
}
|
||||
}
|
||||
|
||||
/* DateInterval of time between subscription start and now */
|
||||
$timeSinceStartOfCurrentPeriod = $startOfCurrentPeriod->diff( \IPS\DateTime::create() );
|
||||
|
||||
/* Count Days in Period */
|
||||
$daysUsedInPeriod = new \IPS\Math\Number( (string)\IPS\DateTime::intervalToDays( $timeSinceStartOfCurrentPeriod ) );
|
||||
|
||||
/* Count how many days we have left to our current subscription so we can calculate how much we are owed */
|
||||
$daysRemainingInPeriod = $numberOfDaysInCurrentRenewalTerm->subtract( $daysUsedInPeriod );
|
||||
$valueRemainingInSubscription = $purchase->renewals ? $purchase->renewals->costPerDay()->amount->multiply( $daysRemainingInPeriod ) : new \IPS\Math\Number( '0' );
|
||||
|
||||
/* Start with 0 and subtract what we are still owed */
|
||||
$cost = new \IPS\Math\Number( "0" );
|
||||
$cost = $cost->subtract( $valueRemainingInSubscription );
|
||||
|
||||
/* Now add the cost of the new package */
|
||||
$valueRemainingInThisPackage = $renewalTermToUse->costPerDay()->amount->multiply( $daysRemainingInPeriod );
|
||||
$cost = $cost->add( $valueRemainingInThisPackage );
|
||||
|
||||
/* But what if our current expiration is multiple cycles ahead? */
|
||||
if( $purchase->expire instanceof \IPS\DateTime and $purchase->renewals->days() < $renewalTermToUse->days())
|
||||
{
|
||||
$calculatedExpiration = $purchase->expire->sub( $renewalTermToUse->interval );
|
||||
while( $calculatedExpiration->diff( \IPS\DateTime::create() )->invert )
|
||||
{
|
||||
$cost = $cost->add( new \IPS\Math\Number( $priceOfThisPackage ) );
|
||||
$calculatedExpiration = $calculatedExpiration->sub( $renewalTermToUse->interval );
|
||||
}
|
||||
}
|
||||
|
||||
return new \IPS\nexus\Money( $cost, $currency );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -887,7 +943,7 @@ class _Package extends \IPS\Node\Model
|
||||
$purchase->expire = NULL;
|
||||
}
|
||||
/* 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'] ) )
|
||||
elseif ( $purchase->expire and $term and $oldTerm and ( $rawTerm['unit'] != $oldTerm['unit'] or $rawTerm['term'] != $oldTerm['term'] ) )
|
||||
{
|
||||
/* 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 )
|
||||
@@ -927,7 +983,7 @@ class _Package extends \IPS\Node\Model
|
||||
$purchase->save();
|
||||
|
||||
/* Re-add usergroups */
|
||||
$newPackage->_addUsergroups( $purchase->member );
|
||||
$newPackage->_addUsergroups( $purchase->member, TRUE );
|
||||
|
||||
/* Cancel any pending invoices */
|
||||
if ( $pendingInvoice = $purchase->invoice_pending )
|
||||
@@ -1132,13 +1188,20 @@ class _Package extends \IPS\Node\Model
|
||||
* Add user groups
|
||||
*
|
||||
* @param \IPS\nexus\Customer $member The customer
|
||||
* @param bool $isPackageChange true if this is an upgrade/downgrade
|
||||
* @return void
|
||||
*/
|
||||
public function _addUsergroups( $member )
|
||||
public function _addUsergroups( $member, $isPackageChange=false )
|
||||
{
|
||||
$previousGroup = 0;
|
||||
$previousSecondary = '';
|
||||
$newSecondary = '';
|
||||
$current = null;
|
||||
try
|
||||
{
|
||||
$current = \IPS\Db::i()->select( 'sub_previous_group,sub_previous_secondary_groups', 'nexus_member_subscriptions', array( 'sub_active=? and sub_member_id=? and sub_package_id=?', 1, $member->member_id, $this->id ) )->first();
|
||||
}
|
||||
catch( \UnderflowException ){}
|
||||
|
||||
/* Primary Group */
|
||||
if ( $this->primary_group and $this->primary_group != $member->member_group_id and !\in_array( $member->member_group_id, explode( ',', \IPS\Settings::i()->nexus_subs_exclude_groups ) ) )
|
||||
@@ -1199,7 +1262,21 @@ class _Package extends \IPS\Node\Model
|
||||
$member->logHistory( 'core', 'group', array( 'type' => 'secondary', 'by' => 'subscription', 'action' => 'add', 'id' => $this->id, 'old' => $previousSecondary, 'new' => $newSecondary ) );
|
||||
}
|
||||
|
||||
\IPS\Db::i()->update( 'nexus_member_subscriptions', array( 'sub_previous_group' => $previousGroup, 'sub_previous_secondary_groups' => $previousSecondary ), array( 'sub_active=1 and sub_package_id=? and sub_member_id=?', $this->id, $member->member_id ) );
|
||||
/* We only want to update the group demotions if this is a new subscription,
|
||||
OR if the previous subscription did not have a demotion */
|
||||
$update = [];
|
||||
if( !$isPackageChange OR $current === null OR !$current['sub_previous_group'] )
|
||||
{
|
||||
$update[ 'sub_previous_group' ] = $previousGroup;
|
||||
}
|
||||
if( !$isPackageChange OR $current === null OR !$current['sub_previous_secondary_groups'] )
|
||||
{
|
||||
$update['sub_previous_secondary_groups'] = $previousSecondary;
|
||||
}
|
||||
if( \count( $update ) )
|
||||
{
|
||||
\IPS\Db::i()->update( 'nexus_member_subscriptions', $update, array( 'sub_active=1 and sub_member_id=?', $member->member_id ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -464,7 +464,7 @@ class _Reply extends \IPS\Content\Comment implements \IPS\Content\Hideable
|
||||
break;
|
||||
}
|
||||
|
||||
$email->send( $to, $cc, $bcc, $fromEmail, $fromName, array( 'Message-Id' => "<IPS-{$this->id}-SR{$this->item()->id}.{$this->item()->email_key}-{$fromEmail}>" ) );
|
||||
$email->send( $to, $cc, $bcc, $fromEmail, $fromName, [ 'Message-ID' => "<IPS-{$this->id}-SR{$this->item()->id}.{$this->item()->email_key}-{$fromEmail}>" ] );
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
|
||||
namespace IPS\nexus\Support;
|
||||
|
||||
use IPS\Member;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
@@ -32,6 +34,13 @@ class _Request extends \IPS\Content\Item implements \IPS\Content\ReadMarkers
|
||||
* @brief Application
|
||||
*/
|
||||
public static $application = 'nexus';
|
||||
|
||||
/**
|
||||
* Should IndexNow be skipped for this item? Can be used to prevent that Private Messages,
|
||||
* Reports and other content which is never going to be visible to guests is triggering the requests.
|
||||
* @var bool
|
||||
*/
|
||||
public static bool $skipIndexNow = TRUE;
|
||||
|
||||
/**
|
||||
* @brief Module
|
||||
@@ -590,7 +599,7 @@ class _Request extends \IPS\Content\Item implements \IPS\Content\ReadMarkers
|
||||
$allowedPackageIds = explode( ',', $department->packages );
|
||||
$allowedSubscriptionIds = explode( ',', $department->subscriptions );
|
||||
$allowedPackages = $department->serializedPackageIds();
|
||||
$departmentToggles[ $department->id ][] = 'support_purchase_' . $allowedPackages;
|
||||
$departmentToggles[ $department->id ][] = 'support_purchase_' . $allowedPackages;
|
||||
if ( !isset( $purchaseFields[ $allowedPackages ] ) )
|
||||
{
|
||||
$field = new \IPS\Helpers\Form\Node(
|
||||
@@ -600,11 +609,12 @@ class _Request extends \IPS\Content\Item implements \IPS\Content\ReadMarkers
|
||||
array(
|
||||
'class' => 'IPS\nexus\Purchase',
|
||||
'where' => $purchaseWhere,
|
||||
'permissionCheck' => function( $node ) use ( $allowedPackageIds, $allowedSubscriptionIds )
|
||||
'permissionCheck' => function( $node ) use ( $allowedPackageIds, $allowedSubscriptionIds, $account )
|
||||
{
|
||||
return (
|
||||
return ((
|
||||
( $node->active and $node->app === 'nexus' and ( $node->type === 'package' and \in_array( $node->item_id, $allowedPackageIds ) ) )
|
||||
or ( $node->active and $node->app === 'nexus' and ( $node->type === 'subscription' and \in_array( $node->item_id, $allowedSubscriptionIds ) ) )
|
||||
) and $node->canView( $account )
|
||||
);
|
||||
},
|
||||
'forceOwner' => FALSE,
|
||||
@@ -1951,6 +1961,28 @@ class _Request extends \IPS\Content\Item implements \IPS\Content\ReadMarkers
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save Changed Columns
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
/* There is a rare case where the request can be generated from a guest transaction
|
||||
after the member account has already been created. In those instances,
|
||||
the member ID does not get properly assigned. */
|
||||
if( $this->email and !$this->member )
|
||||
{
|
||||
$member = Member::load( $this->email, 'email' );
|
||||
if( $member->member_id )
|
||||
{
|
||||
$this->member = $member->member_id;
|
||||
}
|
||||
}
|
||||
|
||||
parent::save();
|
||||
}
|
||||
|
||||
/* !Delete */
|
||||
|
||||
|
||||
Reference in new issue
Block a user