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\Gateway;
/* 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;
@@ -41,18 +41,14 @@ class _PayPal extends \IPS\nexus\Gateway
{
$settings = json_decode( $this->settings, TRUE );
/* Card payments can only be certain currency amounts and require name and billing address */
if ( $settings['type'] === 'card' )
{
/* Supported Currencies via https://developer.paypal.com/docs/integration/direct/rest/currency-codes/ */
if ( !\in_array( $amount->currency, array( 'AUD', 'USD', 'CZK', 'DKK', 'HKD', 'ILS', 'CAD', 'EUR', 'MXN', 'NZD', 'NOK', 'PHP', 'PLN', 'JPY', 'GBP', 'RUB', 'SGD', 'SEK', 'CHF', 'THB' ) ) or !$customer->cm_first_name or !$customer->cm_last_name or !$billingAddress )
{
return FALSE;
}
}
/* Card payments require name and billing address */
if ( isset( $settings['type'] ) and $settings['type'] === 'card' and ( !$customer->cm_first_name or !$customer->cm_last_name or !$billingAddress ) )
{
return FALSE;
}
/* Billing agreements require a billing address */
elseif ( count( $recurrings ) == 1 and ( isset( $settings['billing_agreements'] ) and $settings['billing_agreements'] == 'required' ) and !$billingAddress )
elseif ( static::_canProcessRecurringsAsBillingAgreement( $recurrings ) and ( isset( $settings['billing_agreements'] ) and $settings['billing_agreements'] == 'required' ) and !$billingAddress )
{
return FALSE;
}
@@ -221,20 +217,22 @@ class _PayPal extends \IPS\nexus\Gateway
/**
* Can store cards?
*
* @param bool $adminCreatableOnly If TRUE, will only return gateways where the admin (opposed to the user) can create a new option
* @return bool
*/
public function canStoreCards()
public function canStoreCards( $adminCreatableOnly = FALSE )
{
$settings = json_decode( $this->settings, TRUE );
return ( isset( $settings['type'] ) and $settings['type'] === 'card' and $settings['vault'] );
return ( isset( $settings['type'] ) and $settings['type'] === 'card' and isset( $settings['vault'] ) and $settings['vault'] );
}
/**
* Admin can manually charge using this gateway?
*
* @param \IPS\nexus\Customer $customer The customer we're wanting to charge
* @return bool
*/
public function canAdminCharge()
public function canAdminCharge( \IPS\nexus\Customer $customer )
{
$settings = json_decode( $this->settings, TRUE );
return ( isset( $settings['type'] ) and $settings['type'] === 'card' );
@@ -248,7 +246,7 @@ class _PayPal extends \IPS\nexus\Gateway
public function billingAgreements()
{
$settings = json_decode( $this->settings, TRUE );
return ( isset( $settings['type'] ) and $settings['type'] === 'paypal' ) and ( isset( $settings['billing_agreements'] ) and in_array( $settings['billing_agreements'], array( 'required', 'optional' ) ) );
return ( ( isset( $settings['type'] ) and $settings['type'] === 'paypal' ) or !isset( $settings['type'] ) ) and ( isset( $settings['billing_agreements'] ) and \in_array( $settings['billing_agreements'], array( 'required', 'optional' ) ) );
}
/* !Payment Gateway */
@@ -256,24 +254,25 @@ class _PayPal extends \IPS\nexus\Gateway
/**
* Payment Screen Fields
*
* @param \IPS\nexus\Invoice $invoice Invoice
* @param \IPS\nexus\Money $amount The amount to pay now
* @param \IPS\nexus\Customer $member The member the payment screen is for (if in the ACP charging to a member's card) or NULL for currently logged in member
* @param array $recurrings Details about recurring costs
* @param \IPS\nexus\Invoice $invoice Invoice
* @param \IPS\nexus\Money $amount The amount to pay now
* @param \IPS\nexus\Customer $member The member the payment screen is for (if in the ACP charging to a member's card) or NULL for currently logged in member
* @param array $recurrings Details about recurring costs
* @param bool $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
*/
public function paymentScreen( \IPS\nexus\Invoice $invoice, \IPS\nexus\Money $amount, \IPS\nexus\Customer $member = NULL, $recurrings = array() )
public function paymentScreen( \IPS\nexus\Invoice $invoice, \IPS\nexus\Money $amount, \IPS\nexus\Customer $member = NULL, $recurrings = array(), $type = 'checkout' )
{
$settings = json_decode( $this->settings, TRUE );
if ( $settings['type'] === 'card' )
if ( isset( $settings['type'] ) and $settings['type'] === 'card' )
{
return array( 'card' => new \IPS\nexus\Form\CreditCard( $this->id . '_card', NULL, TRUE, array(
'types' => array( \IPS\nexus\CreditCard::TYPE_VISA, \IPS\nexus\CreditCard::TYPE_MASTERCARD, \IPS\nexus\CreditCard::TYPE_DISCOVER, \IPS\nexus\CreditCard::TYPE_AMERICAN_EXPRESS ),
'save' => $settings['vault'] ? $this : NULL,
'save' => ( isset( $settings['vault'] ) and $settings['vault'] ) ? $this : NULL,
'member' => $member
) ) );
}
elseif ( $settings['billing_agreements'] == 'optional' and count( $recurrings ) == 1 and $invoice->billaddress )
elseif ( isset( $settings['billing_agreements'] ) and $settings['billing_agreements'] == 'optional' and static::_canProcessRecurringsAsBillingAgreement( $recurrings ) and $invoice->billaddress )
{
return array( 'billing_agreement' => new \IPS\Helpers\Form\Checkbox( 'paypal_billing_agreement', TRUE, FALSE ) );
}
@@ -287,23 +286,24 @@ class _PayPal extends \IPS\nexus\Gateway
* @param array|\IPS\nexus\Customer\CreditCard $values Values from form OR a stored card object if this gateway supports them
* @param \IPS\nexus\Fraud\MaxMind\Request|NULL $maxMind *If* MaxMind is enabled, the request object will be passed here so gateway can additional data before request is made
* @param array $recurrings Details about recurring costs
* @param string|NULL $source 'checkout' if the customer is doing this at a normal checkout, 'renewal' is an automatically generated renewal invoice, 'manual' is admin manually charging. NULL is unknown
* @return \IPS\DateTime|NULL Auth is valid until or NULL to indicate auth is good forever
* @throws \LogicException Message will be displayed to user
*/
public function auth( \IPS\nexus\Transaction $transaction, $values, \IPS\nexus\Fraud\MaxMind\Request $maxMind = NULL, $recurrings = array() )
public function auth( \IPS\nexus\Transaction $transaction, $values, \IPS\nexus\Fraud\MaxMind\Request $maxMind = NULL, $recurrings = array(), $source = NULL )
{
/* We need a transaction ID */
$transaction->save();
/* Do it */
$settings = json_decode( $this->settings, TRUE );
if ( $settings['type'] === 'card' )
if ( isset( $settings['type'] ) and $settings['type'] === 'card' )
{
return $this->_cardAuth( is_array( $values ) ? $values[ $this->id . '_card' ] : $values, $transaction, $maxMind );
return $this->_cardAuth( \is_array( $values ) ? $values[ $this->id . '_card' ] : $values, $transaction, $maxMind );
}
else
{
if ( count( $recurrings ) == 1 and ( $settings['billing_agreements'] == 'required' or ( $settings['billing_agreements'] == 'optional' and $values['paypal_billing_agreement'] ) ) )
if ( static::_canProcessRecurringsAsBillingAgreement( $recurrings ) and ( $settings['billing_agreements'] == 'required' or ( $settings['billing_agreements'] == 'optional' and $values['paypal_billing_agreement'] ) ) )
{
foreach ( $recurrings as $recurrance )
{
@@ -381,8 +381,8 @@ class _PayPal extends \IPS\nexus\Gateway
'credit_card' => array(
'number' => $card->number,
'type' => $cardType,
'expire_month' => intval( $card->expMonth ),
'expire_year' => intval( $card->expYear ),
'expire_month' => \intval( $card->expMonth ),
'expire_year' => \intval( $card->expYear ),
'cvv2' => $card->ccv,
'first_name' => $this->_getFirstName( $transaction ),
'last_name' => $this->_getLastName( $transaction ),
@@ -473,6 +473,8 @@ class _PayPal extends \IPS\nexus\Gateway
*/
protected function _billingAgreementAuth( \IPS\nexus\Transaction $transaction, \IPS\nexus\Fraud\MaxMind\Request $maxMind = NULL, \IPS\nexus\Purchase\RenewalTerm $term, $items )
{
$settings = json_decode( $this->settings, TRUE );
/* Work out the name */
$titles = array();
foreach ( $items as $item )
@@ -480,110 +482,125 @@ class _PayPal extends \IPS\nexus\Gateway
$titles[] = ( $item->name . ( $item->quantity > 1 ? " x{$item->quantity}" : '' ) );
}
$title = implode( ', ', $titles );
if ( mb_strlen( $title ) > 128 )
if ( mb_strlen( $title ) > 127 )
{
$title = mb_substr( $title, 0, 125 ) . '...';
$title = mb_substr( $title, 0, 124 ) . '...';
}
$description = sprintf( $transaction->member->language()->get('transaction_number'), $transaction->id );
/* Create Billing Plan */
$paymentDefinitions = array();
$definition = array(
'name' => 'Payment Definition',
'type' => 'REGULAR',
/* Create a product */
$product = $this->api( 'catalogs/products', array(
'name' => $title,
'description' => $description,
'type' => 'DIGITAL',
) );
/* Create a plan */
$renewalAmountIncludingTax = $term->cost->amount->multiply( new \IPS\Math\Number( $term->tax ? ( (string) ( 1 + $term->tax->rate( $transaction->invoice->billaddress ) ) ) : '1' ) );
$billingCycle = array(
'pricing_scheme' => array(
'fixed_price' => array(
'currency_code' => $term->cost->currency,
'value' => ( new \IPS\nexus\Money( $renewalAmountIncludingTax, $term->cost->currency ) )->amountAsString()
),
),
'frequency' => array(),
'tenure_type' => 'REGULAR',
'sequence' => 1,
'total_cycles' => 0
);
if ( $term->interval->y )
{
$definition['frequency_interval'] = $term->interval->y;
$definition['frequency'] = 'YEAR';
$billingCycle['frequency']['interval_unit'] = 'YEAR';
$billingCycle['frequency']['interval_count'] = $term->interval->y;
}
elseif ( $term->interval->m )
{
$definition['frequency_interval'] = $term->interval->m;
$definition['frequency'] = 'MONTH';
$billingCycle['frequency']['interval_unit'] = 'MONTH';
$billingCycle['frequency']['interval_count'] = $term->interval->m;
}
elseif ( $term->interval->d )
{
$definition['frequency_interval'] = $term->interval->d;
$definition['frequency'] = 'DAY';
if ( $term->interval->d % 7 == 0 )
{
$billingCycle['frequency']['interval_unit'] = 'WEEK';
$billingCycle['frequency']['interval_count'] = $term->interval->d / 7;
}
else
{
$billingCycle['frequency']['interval_unit'] = 'DAY';
$billingCycle['frequency']['interval_count'] = $term->interval->d;
}
}
$definition['cycles'] = '0';
$definition['amount'] = array(
'currency' => $term->cost->currency,
'value' => $term->cost->amountAsString()
$planDetails = array(
'product_id' => $product['id'],
'name' => $title,
'description' => $description,
'billing_cycles' => array(),
'payment_preferences' => array(
'auto_bill_outstanding' => TRUE,
'payment_failure_threshold' => \intval( $settings['billing_agreement_allowed_fails'] )
),
'taxes' => array(
'percentage' => $term->tax ? ( $term->tax->rate( $transaction->invoice->billaddress ) * 100 ) : '0',
'inclusive' => TRUE
),
'quantity_supported' => FALSE
);
if ( $term->tax )
if ( $transaction->amount->amount->compare( $renewalAmountIncludingTax ) === 0 )
{
$taxableAmount = $term->cost->amount;
$taxRate = new \IPS\Math\Number( (string) $term->tax->rate( $transaction->invoice->billaddress ) );
$taxAmount = new \IPS\nexus\Money( $taxableAmount->multiply( $taxRate ), $term->cost->currency );
$definition['charge_models'] = array(
array(
'type' => 'TAX',
'amount' => array(
'currency' => $term->cost->currency,
'value' => $taxAmount->amountAsString()
)
),
);
$planDetails['billing_cycles'][] = $billingCycle;
}
$paymentDefinitions[] = $definition;
$response = $this->api( 'payments/billing-plans', array(
'name' => $description,
'description' => $title,
'type' => 'INFINITE',
'payment_definitions' => $paymentDefinitions,
'merchant_preferences' => array(
'setup_fee' => array(
'currency' => $transaction->amount->currency,
'value' => $transaction->amount->amountAsString(),
else
{
$planDetails['billing_cycles'][] = array(
'pricing_scheme' => array(
'fixed_price' => array(
'currency_code' => $transaction->amount->currency,
'value' => $transaction->amount->amountAsString()
),
),
'cancel_url' => (string) $transaction->invoice->checkoutUrl(),
'return_url' => \IPS\Settings::i()->base_url . 'applications/nexus/interface/gateways/paypal.php?billingAgreement=1&nexusTransactionId=' . $transaction->id,
'initial_fail_amount_action' => 'CANCEL',
'frequency' => $billingCycle['frequency'],
'tenure_type' => 'TRIAL',
'sequence' => 1,
'total_cycles' => 1
);
$billingCycle['sequence'] = 2;
$planDetails['billing_cycles'][] = $billingCycle;
}
$plan = $this->api( 'billing/plans', $planDetails );
/* Create a subscription */
$subscription = $this->api( 'billing/subscriptions', array(
'plan_id' => $plan['id'],
'quantity' => '1',
'subscriber' => array(
'name' => array(
'given_name' => $this->_getFirstName( $transaction ),
'surname' => $this->_getLastName( $transaction )
),
'email_address' => $transaction->member->email,
),
'application_context' => array(
'brand_name' => \IPS\Settings::i()->board_name,
'locale' => $transaction->member->language()->bcp47(),
'shipping_preference' => 'NO_SHIPPING',
'user_action' => 'SUBSCRIBE_NOW',
'payment_method' => array(
'payer_selected' => 'PAYPAL',
'payee_preferred' => 'UNRESTRICTED',
'category' => 'CUSTOMER_PRESENT_RECURRING_FIRST',
),
'cancel_url' => (string) $transaction->invoice->checkoutUrl(),
'return_url' => \IPS\Settings::i()->base_url . 'applications/nexus/interface/gateways/paypal.php?subscription=1&nexusTransactionId=' . $transaction->id,
)
) );
$billingPlanId = $response['id'];
/* Activate it */
$response = $this->api( 'payments/billing-plans/' . $billingPlanId, array(
array(
'path' => '/',
'value' => array(
'state' => 'ACTIVE'
),
'op' => 'replace',
)
), 'patch' );
/* Create Billing Agreement */
$payerInfo = array( 'email' => $transaction->member->email );
if ( $transaction->member->cm_phone )
{
$payerInfo['phone'] = $transaction->member->cm_phone;
}
$payerInfo['billing_address'] = $this->_getAddress( $transaction->invoice->billaddress, $transaction->invoice->member );
$billingAgreementData = array(
'name' => $description,
'description' => $title,
'start_date' => \IPS\DateTime::create()->add( $term->interval )->rfc3339(),
'payer' => array(
'payment_method' => 'paypal',
'payer_info' => $payerInfo,
)
);
if ( $transaction->invoice->shipaddress )
{
$billingAgreementData['shipping_address'] = $this->_getAddress( $transaction->invoice->shipaddress, $transaction->invoice->member );
}
$billingAgreementData['plan'] = array( 'id' => $billingPlanId );
$response = $this->api( 'payments/billing-agreements', $billingAgreementData );
/* Redirect */
foreach ( $response['links'] as $link )
foreach ( $subscription['links'] as $link )
{
if ( $link['rel'] === 'approval_url' )
if ( $link['rel'] === 'approve' )
{
\IPS\Output::i()->redirect( \IPS\Http\Url::external( $link['href'] ) );
}
@@ -628,7 +645,22 @@ class _PayPal extends \IPS\nexus\Gateway
}
else
{
$authId = $transaction->gw_id;
if( $transaction->billing_agreement )
{
try
{
$sale = $this->api( "payments/sale/{$transaction->gw_id}", NULL, 'get' );
return $this->refund( $transaction ); // "Sales" came from Billing Agreements and have already been captured
}
catch ( \IPS\nexus\Gateway\PayPal\Exception $e )
{
$authId = $transaction->gw_id;
}
}
else
{
$authId = $transaction->gw_id;
}
}
/* Void it */
@@ -644,37 +676,6 @@ class _PayPal extends \IPS\nexus\Gateway
*/
public function capture( \IPS\nexus\Transaction $transaction )
{
/* If this is for a billing agreement, it should have automatically been captured - all we need to do is check its status, and if
it has been captured, record the ID - otherwise return an error explaining what's going on.
The only time we normally get here is if its the initial transaction and PayPal didn't take payment before the user was redirected back
(in which case the status will be STATUS_GATEWAY_PENDING) but if has been an issue (with Maxmind, for example) it could be STATUS_HELD */
if ( $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 )
{
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' )
{
$transaction->gw_id = $t['transaction_id'];
$transaction->save();
return TRUE;
}
elseif ( $t['status'] == 'Refunded' )
{
$response = 'RFND';
}
elseif ( $t['status'] == 'Failed' )
{
$response = 'FAIL';
}
}
}
throw new \RuntimeException( $response );
}
/* Try to find the authorization ID */
if ( mb_substr( $transaction->gw_id, 0, 4 ) === 'PAY-' )
{
@@ -695,7 +696,22 @@ class _PayPal extends \IPS\nexus\Gateway
}
else
{
$authId = $transaction->gw_id;
if( $transaction->billing_agreement )
{
try
{
$sale = $this->api( "payments/sale/{$transaction->gw_id}", NULL, 'get' );
return TRUE; // "Sales" came from Billing Agreements and have already been captured
}
catch ( \IPS\nexus\Gateway\PayPal\Exception $e )
{
$authId = $transaction->gw_id;
}
}
else
{
$authId = $transaction->gw_id;
}
}
/* Capture it */
@@ -797,13 +813,38 @@ class _PayPal extends \IPS\nexus\Gateway
{
$settings = json_decode( $this->settings, TRUE );
$form->add( new \IPS\Helpers\Form\Radio( 'paypal_type', $settings['type'], TRUE, array( 'options' => array( 'paypal' => 'paypal_type_paypal', 'card' => 'paypal_type_card' ), 'toggles' => array( 'paypal' => array( 'paypal_billing_agreements' ), 'card' => array( 'paypal_vault' ) ) ) ) );
$form->add( new \IPS\Helpers\Form\Radio( 'paypal_billing_agreements', $this->id ? ( (string) $settings['billing_agreements'] ) : 'optional', FALSE, array( 'options' => array(
'required' => 'paypal_billing_agreements_req',
'optional' => 'paypal_billing_agreements_opt',
'' => 'paypal_billing_agreements_dis',
) ), NULL, NULL, NULL, 'paypal_billing_agreements' ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'paypal_vault', $this->id ? $settings['vault'] : TRUE, FALSE, array(), NULL, NULL, NULL, 'paypal_vault' ) );
if ( isset( $settings['type'] ) and $settings['type'] === 'card' )
{
$form->add( new \IPS\Helpers\Form\Radio( 'paypal_type', $settings['type'], TRUE, array( 'options' => array( 'paypal' => 'paypal_type_paypal', 'card' => 'paypal_type_card' ), 'toggles' => array( 'paypal' => array( 'paypal_billing_agreements' ), 'card' => array( 'paypal_vault' ) ) ) ) );
}
$form->add( new \IPS\Helpers\Form\Radio( 'paypal_billing_agreements', ( $this->id AND isset( $settings['billing_agreements'] ) ) ? (string) $settings['billing_agreements'] : '', FALSE, array(
'options' => array(
'required' => 'paypal_billing_agreements_req',
'optional' => 'paypal_billing_agreements_opt',
'' => 'paypal_billing_agreements_dis',
),
'toggles' => array(
'required' => array( 'paypal_billing_agreement_allowed_fails' ),
'optional' => array( 'paypal_billing_agreement_allowed_fails' ),
)
), function( $val ) {
if ( $val )
{
if ( \IPS\Http\Url::internal('')->data['scheme'] !== 'https' )
{
throw new \DomainException('paypal_billing_agreements_https');
}
}
}, NULL, NULL, 'paypal_billing_agreements' ) );
$form->add( new \IPS\Helpers\Form\Number( 'paypal_billing_agreement_allowed_fails', ( $this->id AND isset( $settings['billing_agreement_allowed_fails'] ) ) ? $settings['billing_agreement_allowed_fails'] : 0, FALSE, array( 'unlimited' => 0, 'min' => 1 ), NULL, \IPS\Member::loggedIn()->language()->addToStack('paypal_billing_agreement_allowed_fails_prefix'), \IPS\Member::loggedIn()->language()->addToStack('paypal_billing_agreement_allowed_fails_suffix'), 'paypal_billing_agreement_allowed_fails' ) );
if ( isset( $settings['type'] ) and $settings['type'] === 'card' )
{
$form->add( new \IPS\Helpers\Form\YesNo( 'paypal_vault', ( $this->id and isset( $settings['vault'] ) ) ? $settings['vault'] : TRUE, FALSE, array(), NULL, NULL, NULL, 'paypal_vault' ) );
}
$form->add( new \IPS\Helpers\Form\Text( 'paypal_client_id', $settings['client_id'], TRUE ) );
$form->add( new \IPS\Helpers\Form\Text( 'paypal_secret', $settings['secret'], TRUE ) );
}
@@ -819,13 +860,51 @@ class _PayPal extends \IPS\nexus\Gateway
{
try
{
$this->getNewToken( $settings );
$token = $this->getNewToken( $settings );
$settings['token'] = $token['access_token'];
$settings['token_expire'] = ( time() + $token['expires_in'] );
if ( isset( $settings['billing_agreements'] ) and $settings['billing_agreements'] )
{
$correctWebhookUrl = \IPS\Settings::i()->base_url . 'applications/nexus/interface/gateways/paypal-webhook.php';
$webhookId = NULL;
$webhooks = $this->api( 'notifications/webhooks', NULL, 'get', TRUE, $settings );
foreach ( $webhooks['webhooks'] as $webhook )
{
if ( $webhook['url'] === $correctWebhookUrl )
{
foreach ( $webhook['event_types'] as $eventType )
{
if ( $eventType['name'] === '*' )
{
$webhookId = $webhook['id'];
break 2;
}
}
}
}
if ( !$webhookId )
{
$response = $this->api( 'notifications/webhooks', array(
'url' => $correctWebhookUrl,
'event_types' => array(
array(
'name' => '*'
)
)
), 'post', TRUE, $settings );
$webhookId = $response['id'];
}
$settings['webhook_id'] = $webhookId;
}
\IPS\core\AdminNotification::remove( 'nexus', 'ConfigurationError', "pm{$this->id}" );
}
catch ( \Exception $e )
{
throw new \InvalidArgumentException( $e->getMessage() ?: \IPS\Member::loggedIn()->language()->addToStack('paypal_connection_error'), $e->getCode() );
}
return $settings;
}
@@ -834,24 +913,27 @@ class _PayPal extends \IPS\nexus\Gateway
/**
* Send API Request
*
* @param string $uri The API to request (e.g. "payments/payment")
* @param array $data The data to send
* @param string $method Method (get/post)
* @parsam bool $expectResponse
* @param string $uri The API to request (e.g. "payments/payment")
* @param array $data The data to send
* @param string $method Method (get/post)
* @param array|NULL $settings Settings (NULL for saved setting)
* @return array|null
* @throws \IPS\Http|Exception
* @throws \IPS\nexus\Gateway\PayPal\Exception
*/
public function api( $uri, $data=NULL, $method='post', $expectResponse=TRUE )
public function api( $uri, $data=NULL, $method='post', $expectResponse=TRUE, $settings=NULL )
{
$settings = json_decode( $this->settings, TRUE );
if ( !isset( $settings['token'] ) or $settings['token_expire'] < time() )
if ( !$settings )
{
$token = $this->getNewToken();
$settings['token'] = $token['access_token'];
$settings['token_expire'] = ( time() + $token['expires_in'] );
$this->settings = json_encode( $settings );
$this->save();
$settings = json_decode( $this->settings, TRUE );
if ( !isset( $settings['token'] ) or $settings['token_expire'] < time() )
{
$token = $this->getNewToken();
$settings['token'] = $token['access_token'];
$settings['token_expire'] = ( time() + $token['expires_in'] );
$this->settings = json_encode( $settings );
$this->save();
}
}
$response = \IPS\Http\Url::external( 'https://' . ( \IPS\NEXUS_TEST_GATEWAYS ? 'api.sandbox.paypal.com' : 'api.paypal.com' ) . '/v1/' . $uri )
@@ -869,7 +951,7 @@ class _PayPal extends \IPS\nexus\Gateway
throw new \IPS\nexus\Gateway\PayPal\Exception( $response, mb_substr( $uri, -7 ) === '/refund' );
}
if ( in_array( $method, array( 'delete', 'patch' ) ) or $response->httpResponseCode == 204 )
if ( \in_array( $method, array( 'delete', 'patch' ) ) or $response->httpResponseCode == 204 )
{
return NULL;
}
@@ -1032,4 +1114,25 @@ class _PayPal extends \IPS\nexus\Gateway
return $payPalTransactionData;
}
/**
* Can we handle the renewal terms in a billing agreement?
*
* @param array $recurrings Details about recurring costs
* @return array
*/
protected static function _canProcessRecurringsAsBillingAgreement( array $recurrings )
{
if ( \count( $recurrings ) == 1 )
{
$recurrance = array_pop( $recurrings );
if ( $recurrance['dueDate']->getTimestamp() === \IPS\DateTime::create()->add( $recurrance['term']->interval )->getTimestamp() )
{
return TRUE;
}
}
return FALSE;
}
}