Version 5.0.0 beta 1

This commit is contained in:
Neo committed 2025-12-19 16:27:35 -08:00
1 parent 25ddeb65d6
commit 15c7beabc5
6736 files changed
+627902 -497943

No files matched your search

@@ -1,59 +0,0 @@
<?php
/**
* @brief 2Checkout Gateway
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @subpackage Nexus
* @since 18 Mar 2014
*/
\define('REPORT_EXCEPTIONS', TRUE);
require_once '../../../../init.php';
try
{
$transaction = \IPS\nexus\Transaction::load( \IPS\Request::i()->nexustransactionid );
if ( $transaction->status !== \IPS\nexus\Transaction::STATUS_PENDING )
{
throw new \OutOfRangeException();
}
}
catch ( \OutOfRangeException $e )
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=nexus&module=checkout&controller=checkout&do=transaction&id=&t=" . \IPS\Request::i()->nexustransactionid, 'front', 'nexus_checkout' ) );
}
$settings = json_decode( $transaction->method->settings, TRUE );
$hash = mb_strtoupper( md5( $settings['word'] . $settings['sid'] . ( \IPS\NEXUS_TEST_GATEWAYS ? 1 : \IPS\Request::i()->order_number ) . number_format( $transaction->amount->amountAsString(), 2 ) ) );
if ( !\IPS\Login::compareHashes( (string) $hash, (string) \IPS\Request::i()->key ) )
{
\IPS\Output::i()->redirect( $transaction->invoice->checkoutUrl()->setQueryString( array( '_step' => 'checkout_pay', 'err' => $transaction->member->language()->get('gateway_err') ) ) );
}
if ( \IPS\Request::i()->credit_card_processed !== 'Y' )
{
\IPS\Output::i()->redirect( $transaction->invoice->checkoutUrl()->setQueryString( array( '_step' => 'checkout_pay', 'err' => $transaction->member->language()->get('card_refused') ) ) );
}
$transaction->gw_id = \IPS\Request::i()->order_number;
$transaction->save();
$maxMind = NULL;
if ( \IPS\Settings::i()->maxmind_key and ( !\IPS\Settings::i()->maxmind_gateways or \IPS\Settings::i()->maxmind_gateways == '*' or \in_array( $transaction->method->id, explode( ',', \IPS\Settings::i()->maxmind_gateways ) ) ) )
{
$maxMind = new \IPS\nexus\Fraud\MaxMind\Request( FALSE );
$maxMind->setIpAddress( $transaction->ip );
$maxMind->setTransaction( $transaction );
}
$memberJustCreated = $transaction->checkFraudRulesAndCapture( $maxMind );
$transaction->sendNotification();
if ( $memberJustCreated )
{
\IPS\Session::i()->setMember( $memberJustCreated );
\IPS\Member\Device::loadOrCreate( $memberJustCreated, FALSE )->updateAfterAuthentication( NULL );
}
\IPS\Output::i()->redirect( $transaction->url() );
@@ -1,138 +0,0 @@
<?php
/**
* @brief Authorize.Net DPM Gateway
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @subpackage Nexus
* @since 17 Mar 2014
*/
\define('REPORT_EXCEPTIONS', TRUE);
require_once '../../../../init.php';
\IPS\Output::setCacheTime( false );
try
{
$invoice = \IPS\nexus\Invoice::load( \IPS\Request::i()->x_invoice_num );
}
catch ( \Exception $e )
{
\IPS\Output::i()->sendOutput( '', 404 );
exit;
}
try
{
/* Load Gateway */
$gateway = \IPS\nexus\Gateway::load( \IPS\Request::i()->payment_method );
if ( !( $gateway instanceof \IPS\nexus\Gateway\AuthorizeNet ) )
{
throw new \Exception( $invoice->member->language()->addToStack('gateway_err') );
}
$settings = json_decode( $gateway->settings, TRUE );
/* Check hash */
$message = '^' . implode( '^', array(
\IPS\Request::i()->x_trans_id,
\IPS\Request::i()->x_test_request,
\IPS\Request::i()->x_response_code,
\IPS\Request::i()->x_auth_code,
\IPS\Request::i()->x_cvv2_resp_code,
\IPS\Request::i()->x_cavv_response,
\IPS\Request::i()->x_avs_code,
\IPS\Request::i()->x_method,
\IPS\Request::i()->x_account_number,
\IPS\Request::i()->x_amount,
\IPS\Request::i()->x_company,
\IPS\Request::i()->x_first_name,
\IPS\Request::i()->x_last_name,
\IPS\Request::i()->x_address,
\IPS\Request::i()->x_city,
\IPS\Request::i()->x_state,
\IPS\Request::i()->x_zip,
\IPS\Request::i()->x_country,
\IPS\Request::i()->x_phone,
\IPS\Request::i()->x_fax,
\IPS\Request::i()->x_email,
\IPS\Request::i()->x_ship_to_company,
\IPS\Request::i()->x_ship_to_first_name,
\IPS\Request::i()->x_ship_to_last_name,
\IPS\Request::i()->x_ship_to_address,
\IPS\Request::i()->x_ship_to_city,
\IPS\Request::i()->x_ship_to_state,
\IPS\Request::i()->x_ship_to_zip,
\IPS\Request::i()->x_ship_to_country,
\IPS\Request::i()->x_invoice_num,
) ) . '^';
if ( isset( \IPS\Request::i()->x_SHA2_Hash ) and isset( $settings['signature_key'] ) and $settings['signature_key'] and \IPS\Login::compareHashes( mb_strtoupper( hash_hmac( 'sha512', $message, hex2bin( $settings['signature_key'] ) ) ), \IPS\Request::i()->x_SHA2_Hash ) )
{
// SHA hash is okay
}
elseif ( isset( \IPS\Request::i()->x_MD5_Hash ) and isset( $settings['hash'] ) and $settings['hash'] and \IPS\Login::compareHashes( mb_strtoupper( md5( $settings['hash'] . $settings['login'] . \IPS\Request::i()->x_trans_id . \IPS\Request::i()->x_amount ) ), \IPS\Request::i()->x_MD5_Hash ) )
{
// MD5 hash is okay
}
else
{
throw new \Exception( $invoice->member->language()->addToStack('gateway_err') );
}
/* Was it accepted? */
if ( \IPS\Request::i()->x_response_code != 1 )
{
\IPS\Session\Front::i();
throw new \IPS\nexus\Gateway\AuthorizeNet\Exception( \IPS\Request::i()->x_response_reason_code );
}
/* Create a transaction */
$transaction = new \IPS\nexus\Transaction;
$transaction->member = \IPS\Member::load( \IPS\Request::i()->x_cust_id );
$transaction->invoice = $invoice;
$transaction->method = $gateway;
$transaction->amount = new \IPS\nexus\Money( \IPS\Request::i()->x_amount, $invoice->currency );
$transaction->currency = $invoice->currency;
$transaction->gw_id = \IPS\Request::i()->x_trans_id;
$transaction->ip = isset( \IPS\Request::i()->x_customer_ip ) ? \IPS\Request::i()->x_customer_ip : \IPS\Request::i()->ipAddress();
$extra = $transaction->extra;
$extra['lastFour'] = str_replace( 'X', '', \IPS\Request::i()->x_account_number );
$transaction->extra = $extra;
$transaction->auth = \IPS\DateTime::create()->add( new \DateInterval( 'P30D' ) );
/* Create a MaxMind request */
$maxMind = NULL;
if ( \IPS\Settings::i()->maxmind_key and ( !\IPS\Settings::i()->maxmind_gateways or \IPS\Settings::i()->maxmind_gateways == '*' or \in_array( $transaction->method->id, explode( ',', \IPS\Settings::i()->maxmind_gateways ) ) ) )
{
$maxMind = new \IPS\nexus\Fraud\MaxMind\Request;
$maxMind->setTransaction( $transaction );
$maxMind->setTransactionType( 'creditcard' );
$maxMind->setAVS( \IPS\Request::i()->x_avs_code );
}
/* Check Fraud Rules and capture */
$transaction->checkFraudRulesAndCapture( $maxMind );
$transaction->sendNotification();
/* Show thanks screen */
$url = $transaction->url();
}
catch ( \Exception $e )
{
$url = $invoice->checkoutUrl()->setQueryString( array( '_step' => 'checkout_pay', 'err' => $e->getMessage() ) );
}
?>
<html>
<head>
<script type='text/javascript' charset='utf-8'>
window.location='<?php echo $url; ?>';
</script>
<noscript>
<meta http-equiv='refresh' content='1;url=<?php echo $url; ?>'>
</noscript>
</head>
<body></body>
</html>
@@ -1,92 +0,0 @@
<?php
/**
* @brief Braintree Webhook Handler
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @subpackage Nexus
* @since 18 Dec 2018
*/
\define('REPORT_EXCEPTIONS', TRUE);
require_once '../../../../init.php';
\IPS\Output::setCacheTime( false );
$webhookNotification = NULL;
$transaction = NULL;
foreach ( \IPS\nexus\Gateway::roots() as $method )
{
if ( $method instanceof \IPS\nexus\Gateway\Braintree )
{
try
{
$webhookNotification = $method->gateway()->webhookNotification()->parse( $_POST["bt_signature"], $_POST["bt_payload"] );
if ( isset( $webhookNotification->subject['dispute'] ) and isset( $webhookNotification->subject['dispute']['transaction'] ) )
{
$transaction = \IPS\nexus\Transaction::constructFromData( \IPS\Db::i()->select( '*', 'nexus_transactions', array( 't_gw_id=?', $webhookNotification->subject['dispute']['transaction']['id'] ) )->first() );
if ( $transaction->method->id == $method->id )
{
break;
}
}
}
catch ( \Exception $e ) {}
}
}
if ( !$webhookNotification or !$transaction )
{
if( $webhookNotification AND $webhookNotification->kind == 'check' )
{
\IPS\Output::i()->sendOutput( 'TEST-CORRECT', 200, 'text/plain' );
}
else
{
\IPS\Output::i()->sendOutput( 'INVALID', 500, 'text/plain' );
}
}
switch ( $webhookNotification->kind )
{
case \Braintree_WebhookNotification::DISPUTE_OPENED:
$transaction->status = $transaction::STATUS_DISPUTED;
$extra = $transaction->extra;
$extra['history'][] = array( 's' => $transaction::STATUS_DISPUTED, 'on' => $webhookNotification->subject['dispute']['createdAt']->getTimestamp(), 'ref' => $webhookNotification->subject['dispute']['id'] );
$transaction->extra = $extra;
$transaction->save();
if ( $transaction->member )
{
$transaction->member->log( 'transaction', array(
'type' => 'status',
'status' => $transaction::STATUS_DISPUTED,
'id' => $transaction->id
) );
}
$transaction->invoice->markUnpaid( \IPS\nexus\Invoice::STATUS_CANCELED );
\IPS\core\AdminNotification::send( 'nexus', 'Transaction', $transaction::STATUS_DISPUTED, TRUE, $transaction );
\IPS\Output::i()->sendOutput( 'OK', 200, 'text/plain' );
exit;
case \Braintree_WebhookNotification::DISPUTE_LOST:
$transaction->status = $transaction::STATUS_REFUNDED;
$transaction->save();
exit;
case \Braintree_WebhookNotification::DISPUTE_WON:
$transaction->status = $transaction::STATUS_PAID;
$transaction->save();
if ( !$transaction->invoice->amountToPay()->amount->isGreaterThanZero() )
{
$transaction->invoice->markPaid();
}
exit;
}
\IPS\Output::i()->sendOutput( 'UNEXPECTED_WEBHOOK', 500, 'text/plain' );
@@ -9,11 +9,26 @@
* @since 11 Oct 2019
*/
\define('REPORT_EXCEPTIONS', TRUE);
require_once '../../../../init.php';
\IPS\Session\Front::i();
use IPS\core\AdminNotification;
use IPS\Db;
use IPS\nexus\extensions\nexus\Item\MiscellaneousCharge;
use IPS\nexus\Gateway;
use IPS\nexus\Gateway\PayPal;
use IPS\nexus\Gateway\PayPal\BillingAgreement;
use IPS\nexus\Invoice;
use IPS\nexus\Invoice\Item\Renewal;
use IPS\nexus\Money;
use IPS\nexus\Purchase;
use IPS\nexus\Transaction;
use IPS\Output;
use IPS\Patterns\ActiveRecordIterator;
use IPS\Session\Front;
\IPS\Output::setCacheTime( false );
define('REPORT_EXCEPTIONS', TRUE);
require_once '../../../../init.php';
Front::i();
Output::i()->pageCaching = FALSE;
/**
* PayPal Webhook Handler
@@ -23,17 +38,17 @@ class paypalWebhookHandler
/**
* @brief Raw Webhook Data (as a string)
*/
private $body;
private mixed $body;
/**
* @brief Parsed Webhook Data (as an array)
*/
private $data;
private array $data;
/**
* @brief The payment method
*/
private $method;
private Gateway $method;
/**
* Constructor
@@ -41,7 +56,7 @@ class paypalWebhookHandler
* @param string $body The raw body posted to this script
* @return void
*/
public function __construct( $body )
public function __construct( string $body )
{
$this->body = $body;
$this->data = json_decode( $body, TRUE );
@@ -50,9 +65,9 @@ class paypalWebhookHandler
/**
* Sale completed
*
* @return void
* @return string
*/
public function saleCompleted()
public function saleCompleted() : string
{
/* Is this a billing agreement payment? */
if ( isset( $this->data['resource'] ) and isset( $this->data['resource']['id'] ) and isset( $this->data['resource']['billing_agreement_id'] ) )
@@ -60,39 +75,36 @@ class paypalWebhookHandler
/* Have we seen this transaction before? */
try
{
/* @note SELECT_FROM_WRITE_SERVER transaction race condition */
$transaction = \IPS\Db::i()->select( '*', 'nexus_transactions', array( 't_gw_id=? AND t_method=?', $this->data['resource']['id'], $this->method->id ), flags: \IPS\Db::SELECT_FROM_WRITE_SERVER )->first();
$transaction = Db::i()->select( '*', 'nexus_transactions', array( 't_gw_id=? AND t_method=?', $this->data['resource']['id'], $this->method->id ), flags: Db::SELECT_FROM_WRITE_SERVER )->first();
return 'ALREADY_PROCESSED';
}
catch ( \UnderflowException $e ) {}
catch (UnderflowException ) {}
/* Get the billing agreement */
try
{
/* @note Possible race condition when the webhook is almost instant */
$billingAgreement = \IPS\nexus\Gateway\PayPal\BillingAgreement::constructFromData( \IPS\Db::i()->select( '*', 'nexus_billing_agreements', array( 'ba_gw_id=? AND ba_method=?', $this->data['resource']['billing_agreement_id'], $this->method->id ), flags: \IPS\Db::SELECT_FROM_WRITE_SERVER )->first() );
$billingAgreement = BillingAgreement::constructFromData( Db::i()->select( '*', 'nexus_billing_agreements', array( 'ba_gw_id=? AND ba_method=?', $this->data['resource']['billing_agreement_id'], $this->method->id ) )->first() );
}
catch ( \UnderflowException $e )
catch (UnderflowException )
{
throw new \DomainException('UNKNOWN_BILLING_AGREEMENT');
throw new DomainException('UNKNOWN_BILLING_AGREEMENT');
}
/* Do it */
try
{
/* @note Possible race condition when the webhook is almost instant */
$transaction = \IPS\nexus\Transaction::constructFromData( \IPS\Db::i()->select( '*', 'nexus_transactions', array( 't_billing_agreement=? AND t_method=? AND t_status=?', $billingAgreement->id, $this->method->id, \IPS\nexus\Transaction::STATUS_GATEWAY_PENDING ), flags: \IPS\Db::SELECT_FROM_WRITE_SERVER )->first() );
$transaction = Transaction::constructFromData( Db::i()->select( '*', 'nexus_transactions', array( 't_billing_agreement=? AND t_method=? AND t_status=?', $billingAgreement->id, $this->method->id, Transaction::STATUS_GATEWAY_PENDING ) )->first() );
$transaction->gw_id = $this->data['resource']['id'];
$transaction->save();
return $this->_processInitialBillingAgreementTransaction( $transaction );
}
catch ( \UnderflowException $e )
catch (UnderflowException )
{
$transaction = new \IPS\nexus\Transaction;
$transaction = new Transaction;
$transaction->member = $billingAgreement->member;
$transaction->method = $this->method;
$transaction->amount = new \IPS\nexus\Money( $this->data['resource']['amount']['total'], $this->data['resource']['amount']['currency'] );
$transaction->amount = new Money( $this->data['resource']['amount']['total'], $this->data['resource']['amount']['currency'] );
$transaction->date = new \IPS\DateTime( $this->data['resource']['create_time'] );
$transaction->extra = array( 'automatic' => TRUE );
$transaction->gw_id = $this->data['resource']['id'];
@@ -110,18 +122,18 @@ class paypalWebhookHandler
/**
* Process the initial transaction for a billing agreement
*
* @param \IPS\nexus\Transaction $transaction The transaction
* @param Transaction $transaction The transaction
* @return string
*/
protected function _processInitialBillingAgreementTransaction( \IPS\nexus\Transaction $transaction )
protected function _processInitialBillingAgreementTransaction( Transaction $transaction ) : string
{
/* Take any fraud action */
$fraudResult = $transaction->fraud_blocked ? $transaction->fraud_blocked->action : NULL;
$fraudResult = $transaction->fraud_blocked?->action;
if ( $fraudResult )
{
$transaction->executeFraudAction( $fraudResult, TRUE );
}
if ( !$fraudResult or $fraudResult === \IPS\nexus\Transaction::STATUS_PAID )
if ( !$fraudResult or $fraudResult === Transaction::STATUS_PAID )
{
$transaction->approve();
}
@@ -136,16 +148,16 @@ class paypalWebhookHandler
/**
* Process a reccuring (i.e. not the intitial) transaction for a billing agreement
*
* @param \IPS\nexus\Transaction $transaction The transaction
* @param Transaction $transaction The transaction
* @return string
*/
protected function _processRecurringBillingAgreementTransaction( \IPS\nexus\Transaction $transaction )
protected function _processRecurringBillingAgreementTransaction( Transaction $transaction ) : string
{
/* Get purchases */
$purchases = new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'nexus_purchases', array( 'ps_billing_agreement=?', $transaction->billing_agreement->id ) ), 'IPS\nexus\Purchase' );
$purchases = new ActiveRecordIterator( Db::i()->select( '*', 'nexus_purchases', array( 'ps_billing_agreement=?', $transaction->billing_agreement->id ) ), 'IPS\nexus\Purchase' );
/* Generate an invoice */
$invoice = new \IPS\nexus\Invoice;
$invoice = new Invoice;
$invoice->system = TRUE;
$invoice->date = $transaction->date;
$invoice->currency = $transaction->amount->currency;
@@ -153,15 +165,16 @@ class paypalWebhookHandler
$invoice->billaddress = $transaction->billing_agreement->member->primaryBillingAddress();
foreach ( $purchases as $purchase )
{
/* @var Purchase $purchase */
if( !$purchase->renewals )
{
// Renewals have been cancelled for this purchase since PayPal tried to bill for it.
$amount = new \IPS\nexus\Money( $purchase->renewal_price, $transaction->amount->currency );
$invoice->addItem( new \IPS\nexus\extensions\nexus\Item\MiscellaneousCharge( sprintf( $purchase->member->language()->get('renew_payment_no_ba'), $purchase->name ), $amount ) );
$amount = new Money( $purchase->renewal_price, $transaction->amount->currency );
$invoice->addItem( new MiscellaneousCharge( sprintf( $purchase->member->language()->get('renew_payment_no_ba'), $purchase->name ), $amount ) );
continue;
}
$invoice->addItem( \IPS\nexus\Invoice\Item\Renewal::create( $purchase ) );
$invoice->addItem( Renewal::create( $purchase ) );
}
$invoice->save();
@@ -174,7 +187,7 @@ class paypalWebhookHandler
/* Log */
$invoice->member->log( 'transaction', array(
'type' => 'paid',
'status' => \IPS\nexus\Transaction::STATUS_PAID,
'status' => Transaction::STATUS_PAID,
'id' => $transaction->id,
'invoice_id' => $invoice->id,
'invoice_title' => $invoice->title,
@@ -212,9 +225,9 @@ class paypalWebhookHandler
/**
* Run
*
* @return void
* @return string
*/
public function run( $headers )
public function run() : string
{
/* Do it */
if ( isset( $this->data['event_type'] ) )
@@ -224,9 +237,9 @@ class paypalWebhookHandler
/* Sale completed: Used for automatic billing agreement purchases */
case 'PAYMENT.SALE.COMPLETED':
/* Validate it */
foreach ( \IPS\nexus\Gateway::roots() as $method )
foreach ( Gateway::roots() as $method )
{
if ( $method instanceof \IPS\nexus\Gateway\PayPal )
if ( $method instanceof PayPal )
{
try
{
@@ -246,12 +259,11 @@ class paypalWebhookHandler
$newSettings = $method->testSettings( $settings );
$method->settings = json_encode( $newSettings );
$method->save();
$settings = $newSettings;
}
/* But if that fails, send an AdminCP notification letting the admin know they need to fix it. */
catch ( \Exception $e )
catch (Exception )
{
\IPS\core\AdminNotification::send( 'nexus', 'ConfigurationError', "pm{$method->id}", FALSE );
AdminNotification::send( 'nexus', 'ConfigurationError', "pm{$method->id}", FALSE );
}
}
@@ -268,14 +280,14 @@ class paypalWebhookHandler
}
}
}
catch ( \Exception $e )
catch (Exception )
{
// Do nothing - try the next one
}
}
}
throw new \Exception('COULD_NOT_VALIDATE');
throw new Exception('COULD_NOT_VALIDATE');
// PAYMENTS.PAYMENT.CREATED, PAYMENT.AUTHORIZATION.CREATED and then PAYMENT.CAPTURE.COMPLETED are actually buying something (not a billing agreement payment)
@@ -286,7 +298,7 @@ class paypalWebhookHandler
}
else
{
throw new \Exception('INVALID_HOOK_DATA');
throw new Exception('INVALID_HOOK_DATA');
}
}
}
@@ -302,10 +314,10 @@ try
$headers[ str_replace( '_', '-', mb_substr( $k, 5 ) ) ] = $v;
}
}
$response = $class->run( $headers );
\IPS\Output::i()->sendOutput( $response, 200, 'text/plain' );
$response = $class->run();
Output::i()->sendOutput( $response, 200, 'text/plain' );
}
catch ( \Exception $e )
catch (Exception $e)
{
\IPS\Output::i()->sendOutput( $e->getMessage(), 500, 'text/plain' );
Output::i()->sendOutput( $e->getMessage(), 500, 'text/plain' );
}
@@ -9,40 +9,52 @@
* @since 07 Mar 2014
*/
\define('REPORT_EXCEPTIONS', TRUE);
use IPS\Db;
use IPS\Http\Url;
use IPS\Log;
use IPS\Member\Device;
use IPS\nexus\Gateway\PayPal\BillingAgreement;
use IPS\nexus\Transaction;
use IPS\Output;
use IPS\Request;
use IPS\Session;
use IPS\Session\Front;
use IPS\Settings;
define('REPORT_EXCEPTIONS', TRUE);
require_once '../../../../init.php';
\IPS\Session\Front::i();
Front::i();
/* Load Transaction */
try
{
$transaction = \IPS\nexus\Transaction::load( \IPS\Request::i()->nexusTransactionId );
$transaction = Transaction::load( Request::i()->nexusTransactionId );
if ( $transaction->status !== \IPS\nexus\Transaction::STATUS_PENDING )
if ( $transaction->status !== Transaction::STATUS_PENDING )
{
throw new \OutofRangeException;
throw new OutOfRangeException;
}
}
catch ( \OutOfRangeException $e )
catch (OutOfRangeException )
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=nexus&module=checkout&controller=checkout&do=transaction&id=&t=" . \IPS\Request::i()->nexusTransactionId, 'front', 'nexus_checkout' ) );
Output::i()->redirect( Url::internal( "app=nexus&module=checkout&controller=checkout&do=transaction&id=&t=" . Request::i()->nexusTransactionId, 'front', 'nexus_checkout' ) );
}
$fraudCheck = function( $transaction, $refuseExecute=TRUE ) {
/* Check fraud rules */
$maxMind = NULL;
if ( \IPS\Settings::i()->maxmind_key and ( !\IPS\Settings::i()->maxmind_gateways or \IPS\Settings::i()->maxmind_gateways == '*' or \in_array( $transaction->method->id, explode( ',', \IPS\Settings::i()->maxmind_gateways ) ) ) )
if ( Settings::i()->maxmind_key and ( !Settings::i()->maxmind_gateways or Settings::i()->maxmind_gateways == '*' or in_array( $transaction->method->id, explode( ',', Settings::i()->maxmind_gateways ) ) ) )
{
$maxMind = new \IPS\nexus\Fraud\MaxMind\Request;
$maxMind->setTransaction( $transaction );
$maxMind->setTransactionType( 'paypal' );
}
$fraudResult = $transaction->runFraudCheck( $maxMind );
if ( $refuseExecute AND $fraudResult === \IPS\nexus\Transaction::STATUS_REFUSED )
if ( $refuseExecute AND $fraudResult === Transaction::STATUS_REFUSED )
{
$transaction->executeFraudAction( $fraudResult, FALSE );
$transaction->sendNotification();
\IPS\Output::i()->redirect( $transaction->url() );
Output::i()->redirect( $transaction->url() );
}
return $fraudResult;
@@ -52,67 +64,45 @@ $fraudCheck = function( $transaction, $refuseExecute=TRUE ) {
try
{
/* Subscription */
if ( isset( \IPS\Request::i()->subscription ) )
if ( isset( Request::i()->subscription ) )
{
/* Sometimes PayPal doesn't include the subscription ID, so let's check if we have it in the transaction */
$subscriptionId = \IPS\Request::i()->subscription_id ?? $transaction->gw_id;
/* If we have no subscription ID, but the transaction is already linked to a Billing agreement, use that */
if( empty( \IPS\Request::i()->subscription_id ) )
{
try
{
$billingAgreement = \IPS\nexus\Gateway\PayPal\BillingAgreement::load( \IPS\Db::i()->select( 't_billing_agreement', 'nexus_transactions', [ 't_id=?', $transaction->id ], flags: \IPS\Db::SELECT_FROM_WRITE_SERVER )->first() );
}
catch( \OutOfRangeException | \UnderflowException )
{
/* Log it, along with the whole request, so that we can try to see what's happening */
\IPS\Log::log( "Missing subscription_id " . print_r( \IPS\Request::i(), true ), 'paypal' );
throw new Exception( \IPS\Member::loggedIn()->language()->get( 'billing_agreement_start_error' ) );
}
}
/* Get details */
if( !isset( $billingAgreement ) )
$response = $transaction->method->api( "billing/subscriptions/" . Request::i()->subscription_id, NULL, 'get' );
/* Create Billing Agreement */
try
{
$response = $transaction->method->api( "billing/subscriptions/" . $subscriptionId, NULL, 'get' );
/* Create Billing Agreement */
try
{
/* @note SELECT_FROM_WRITE_SERVER transaction race condition */
$billingAgreement = \IPS\nexus\Gateway\PayPal\BillingAgreement::constructFromData( \IPS\Db::i()->select( '*', 'nexus_billing_agreements', array( 'ba_gw_id=? AND ba_method=?', $response['id'], $transaction->method->id ), flags: \IPS\Db::SELECT_FROM_WRITE_SERVER )->first() );
}
catch ( \UnderflowException $e )
{
$billingAgreement = new \IPS\nexus\Gateway\PayPal\BillingAgreement;
$billingAgreement->gw_id = $response['id'];
$billingAgreement->method = $transaction->method;
$billingAgreement->member = $transaction->member;
$billingAgreement->started = \IPS\DateTime::create();
$billingAgreement->next_cycle = ( new \IPS\DateTime( $response['billing_info']['next_billing_time'] ) );
$billingAgreement->save();
}
$transaction->billing_agreement = $billingAgreement;
$transaction->save();
$billingAgreement = BillingAgreement::constructFromData( Db::i()->select( '*', 'nexus_billing_agreements', array( 'ba_gw_id=? AND ba_method=?', $response['id'], $transaction->method->id ), flags: Db::SELECT_FROM_WRITE_SERVER )->first() );
}
catch (UnderflowException )
{
$billingAgreement = new BillingAgreement;
$billingAgreement->gw_id = $response['id'];
$billingAgreement->method = $transaction->method;
$billingAgreement->member = $transaction->member;
$billingAgreement->started = \IPS\DateTime::create();
$billingAgreement->next_cycle = ( new \IPS\DateTime( $response['billing_info']['next_billing_time'] ) );
$billingAgreement->save();
}
$transaction->billing_agreement = $billingAgreement;
$transaction->save();
/* Check Fraud */
$fraudResult = $fraudCheck( $transaction, FALSE );
/* Just save that we're waiting for the webhook, but also try again in a few minutes because PayPal is flakey */
$transaction->status = \IPS\nexus\Transaction::STATUS_GATEWAY_PENDING;
$transaction->status = Transaction::STATUS_GATEWAY_PENDING;
$transaction->save();
$transaction->sendNotification();
\IPS\Db::i()->update( 'core_tasks', array( 'enabled' => 1 ), "`key`='gatewayPending'" );
\IPS\Output::i()->redirect( $transaction->url() );
Db::i()->update( 'core_tasks', array( 'enabled' => 1 ), "`key`='gatewayPending'" );
Output::i()->redirect( $transaction->url() );
}
/* Billing Agreement (DEPRECATED) */
elseif ( isset( \IPS\Request::i()->billingAgreement ) )
elseif ( isset( Request::i()->billingAgreement ) )
{
/* Execute */
$response = $transaction->method->api( "payments/billing-agreements/" . \IPS\Request::i()->token . "/agreement-execute" );
$response = $transaction->method->api( "payments/billing-agreements/" . Request::i()->token . "/agreement-execute" );
$agreementId = $response['id'];
if ( isset( $response['payer'] ) and isset( $response['payer']['status'] ) )
{
@@ -122,12 +112,12 @@ try
}
/* Create Billing Agreement */
$billingAgreement = new \IPS\nexus\Gateway\PayPal\BillingAgreement;
$billingAgreement = new BillingAgreement;
$billingAgreement->gw_id = $agreementId;
$billingAgreement->method = $transaction->method;
$billingAgreement->member = $transaction->member;
$billingAgreement->started = \IPS\DateTime::create();
$billingAgreement->next_cycle = \IPS\DateTime::create()->add( new \DateInterval( 'P' . $response['plan']['payment_definitions'][0]['frequency_interval'] . mb_substr( $response['plan']['payment_definitions'][0]['frequency'], 0, 1 ) ) );
$billingAgreement->next_cycle = \IPS\DateTime::create()->add( new DateInterval( 'P' . $response['plan']['payment_definitions'][0]['frequency_interval'] . mb_substr( $response['plan']['payment_definitions'][0]['frequency'], 0, 1 ) ) );
$billingAgreement->save();
$transaction->billing_agreement = $billingAgreement;
$transaction->save();
@@ -144,7 +134,7 @@ try
/* Check Fraud Actions */
$fraudResult = $fraudCheck( $transaction );
if ( $fraudResult and $fraudResult !== \IPS\nexus\Transaction::STATUS_PAID )
if ( $fraudResult and $fraudResult !== Transaction::STATUS_PAID )
{
$transaction->executeFraudAction( $fraudResult, TRUE );
}
@@ -161,30 +151,30 @@ try
$memberJustCreated = $transaction->approve();
if ( $memberJustCreated )
{
\IPS\Session::i()->setMember( $memberJustCreated );
\IPS\Member\Device::loadOrCreate( $memberJustCreated, FALSE )->updateAfterAuthentication( NULL );
Session::i()->setMember( $memberJustCreated );
Device::loadOrCreate( $memberJustCreated, FALSE )->updateAfterAuthentication( NULL );
}
}
$transaction->sendNotification();
\IPS\Output::i()->redirect( $transaction->url() );
Output::i()->redirect( $transaction->url() );
}
}
/* Just save that we're waiting for the webhook */
$transaction->status = \IPS\nexus\Transaction::STATUS_GATEWAY_PENDING;
$transaction->status = Transaction::STATUS_GATEWAY_PENDING;
$transaction->save();
$transaction->sendNotification();
\IPS\Db::i()->update( 'core_tasks', array( 'enabled' => 1 ), "`key`='gatewayPending'" );
\IPS\Output::i()->redirect( $transaction->url() );
Db::i()->update( 'core_tasks', array( 'enabled' => 1 ), "`key`='gatewayPending'" );
Output::i()->redirect( $transaction->url() );
}
/* Normal */
else
{
$response = $transaction->method->api( "checkout/orders/{$transaction->gw_id}/authorize", array(
'payer_id' => \IPS\Request::i()->PayerID,
), 'post', TRUE, NULL, NULL, 2 );
'payer_id' => Request::i()->PayerID,
), 'post', TRUE, NULL, md5( $transaction->invoice->checkoutUrl() . ';' . $transaction->id . ';' . $transaction->gw_id ), 2 );
$transaction->gw_id = $response['purchase_units'][0]['payments']['authorizations'][0]['id']; // Was previously a payment ID. This sets it to the actual transaction ID for the authorization. At capture, it will be updated again to the capture transaction ID
$transaction->auth = \IPS\DateTime::ts( strtotime( $response['purchase_units'][0]['payments']['authorizations'][0]['expiration_time'] ) );
if ( isset( $response['payer'] ) and isset( $response['payer']['status'] ) )
@@ -204,23 +194,22 @@ try
{
$transaction->executeFraudAction( $fraudResult, TRUE );
}
if ( !$fraudResult or $fraudResult === \IPS\nexus\Transaction::STATUS_PAID )
if ( !$fraudResult or $fraudResult === Transaction::STATUS_PAID )
{
$memberJustCreated = $transaction->captureAndApprove();
if ( $memberJustCreated )
{
\IPS\Session::i()->setMember( $memberJustCreated );
\IPS\Member\Device::loadOrCreate( $memberJustCreated, FALSE )->updateAfterAuthentication( NULL );
Session::i()->setMember( $memberJustCreated );
Device::loadOrCreate( $memberJustCreated, FALSE )->updateAfterAuthentication( NULL );
}
}
$transaction->sendNotification();
/* Redirect */
\IPS\Output::i()->redirect( $transaction->url() );
Output::i()->redirect( $transaction->url() );
}
catch ( \Exception $e )
catch (Exception $e )
{
$transaction->method->processException( $transaction, $e );
\IPS\Log::log( $e, 'paypal' );
\IPS\Output::i()->redirect( $transaction->invoice->checkoutUrl()->setQueryString( array( '_step' => 'checkout_pay', 'err' => $e->getMessage() ) ) );
Log::log( $e, 'paypal' );
Output::i()->redirect( $transaction->invoice->checkoutUrl()->setQueryString( array( '_step' => 'checkout_pay', 'err' => $e->getMessage() ) ) );
}
@@ -9,52 +9,66 @@
* @since 20 Jul 2017
*/
\define('REPORT_EXCEPTIONS', TRUE);
require_once '../../../../init.php';
\IPS\Session\Front::i();
use IPS\Log;
use IPS\Member;
use IPS\Member\Device;
use IPS\nexus\Gateway;
use IPS\nexus\Gateway\Stripe;
use IPS\nexus\Invoice;
use IPS\nexus\Money;
use IPS\nexus\Transaction;
use IPS\Output;
use IPS\Request;
use IPS\Session;
use IPS\Session\Front;
use IPS\Settings;
\IPS\Output::setCacheTime( false );
define('REPORT_EXCEPTIONS', TRUE);
require_once '../../../../init.php';
Front::i();
Output::i()->pageCaching = FALSE;
/* Get the invoice */
try
{
$invoice = \IPS\nexus\Invoice::load( \IPS\Request::i()->invoice );
$invoice = Invoice::load( Request::i()->invoice );
if ( !$invoice->canView() )
{
throw new \OutOfRangeException;
throw new OutOfRangeException;
}
}
catch ( \OutOfRangeException $e )
catch (OutOfRangeException )
{
\IPS\Output::i()->sendOutput( json_encode( array( 'success' => 0 ) ), 500, 'application/json' );
Output::i()->sendOutput( json_encode( array( 'success' => 0 ) ), 500, 'application/json' );
}
/* Get the gateway */
try
{
$gateway = \IPS\nexus\Gateway::load( \IPS\Request::i()->gateway );
if ( !( $gateway instanceof \IPS\nexus\Gateway\Stripe ) )
$gateway = Gateway::load( Request::i()->gateway );
if ( !( $gateway instanceof Stripe ) )
{
throw new \OutOfRangeException;
throw new OutOfRangeException;
}
}
catch ( \OutOfRangeException $e )
catch (OutOfRangeException )
{
\IPS\Output::i()->sendOutput( json_encode( array( 'success' => 0 ) ), 500, 'application/json' );
Output::i()->sendOutput( json_encode( array( 'success' => 0 ) ), 500, 'application/json' );
}
/* Create a transaction */
$transaction = new \IPS\nexus\Transaction;
$transaction->member = \IPS\Member::loggedIn();
$transaction = new Transaction;
$transaction->member = Member::loggedIn();
$transaction->invoice = $invoice;
$transaction->amount = new \IPS\nexus\Money( \IPS\Request::i()->amount, mb_strtoupper( \IPS\Request::i()->currency ) );
$transaction->ip = \IPS\Request::i()->ipAddress();
$transaction->amount = new Money( Request::i()->amount, mb_strtoupper( Request::i()->currency ) );
$transaction->ip = Request::i()->ipAddress();
$transaction->method = $gateway;
/* Create a MaxMind request */
$maxMind = NULL;
if ( \IPS\Settings::i()->maxmind_key and ( !\IPS\Settings::i()->maxmind_gateways or \IPS\Settings::i()->maxmind_gateways == '*' or \in_array( $transaction->method->id, explode( ',', \IPS\Settings::i()->maxmind_gateways ) ) ) )
if ( Settings::i()->maxmind_key and ( !Settings::i()->maxmind_gateways or Settings::i()->maxmind_gateways == '*' or in_array( $transaction->method->id, explode( ',', Settings::i()->maxmind_gateways ) ) ) )
{
$maxMind = new \IPS\nexus\Fraud\MaxMind\Request;
$maxMind->setTransaction( $transaction );
@@ -64,25 +78,25 @@ if ( \IPS\Settings::i()->maxmind_key and ( !\IPS\Settings::i()->maxmind_gateways
try
{
/* Authorize */
$transaction->auth = $gateway->auth( $transaction, array( "{$gateway->id}_card" => \IPS\Request::i()->token ), $maxMind );
$transaction->auth = $gateway->auth( $transaction, array( "{$gateway->id}_card" => Request::i()->token ), $maxMind );
/* Check Fraud Rules and capture */
$memberJustCreated = $transaction->checkFraudRulesAndCapture( $maxMind );
if ( $memberJustCreated )
{
\IPS\Session::i()->setMember( $memberJustCreated );
\IPS\Member\Device::loadOrCreate( $memberJustCreated, FALSE )->updateAfterAuthentication( NULL );
Session::i()->setMember( $memberJustCreated );
Device::loadOrCreate( $memberJustCreated, FALSE )->updateAfterAuthentication( NULL );
}
}
catch ( \Exception $e )
catch (Exception $e )
{
\IPS\Log::log( $e, 'applepay' );
\IPS\Output::i()->sendOutput( json_encode( array( 'success' => 0 ) ), 500, 'application/json' );
Log::log( $e, 'applepay' );
Output::i()->sendOutput( json_encode( array( 'success' => 0 ) ), 500, 'application/json' );
}
/* Send email receipt */
$transaction->sendNotification();
/* Return */
\IPS\Output::i()->sendOutput( json_encode( array( 'success' => 1, 'url' => (string) $transaction->url() ) ), 200, 'application/json' );
Output::i()->sendOutput( json_encode( array( 'success' => 1, 'url' => (string) $transaction->url() ) ), 200, 'application/json' );
@@ -9,16 +9,26 @@
* @since 20 Jul 2017
*/
\define('REPORT_EXCEPTIONS', TRUE);
require_once '../../../../init.php';
\IPS\Session\Front::i();
use IPS\Http\Url;
use IPS\Member;
use IPS\Member\Device;
use IPS\nexus\Transaction;
use IPS\Output;
use IPS\Request;
use IPS\Session;
use IPS\Session\Front;
use IPS\Settings;
\IPS\Output::setCacheTime( false );
define('REPORT_EXCEPTIONS', TRUE);
require_once '../../../../init.php';
Front::i();
Output::i()->pageCaching = FALSE;
/* Little IN_DEV helper to prevent firing this until we've triggered the webhooks */
if ( \IPS\IN_DEV and !isset( \IPS\Request::i()->indevconfirm ) )
if ( \IPS\IN_DEV and !isset( Request::i()->indevconfirm ) )
{
echo "<a href='" . \IPS\Request::i()->url()->setQueryString( 'indevconfirm', 1 ) . "'>Continue</a>";
echo "<a href='" . Request::i()->url()->setQueryString( 'indevconfirm', 1 ) . "'>Continue</a>";
exit;
}
@@ -28,37 +38,37 @@ sleep( 5 );
/* Load Source */
try
{
$transaction = \IPS\nexus\Transaction::load( \IPS\Request::i()->nexusTransactionId );
$intent = $transaction->method->api( 'payment_intents/' . \IPS\Request::i()->payment_intent, null, 'GET' );
if( $intent['client_secret'] != \IPS\Request::i()->payment_intent_client_secret )
$transaction = Transaction::load( Request::i()->nexusTransactionId );
$intent = $transaction->method->api( 'payment_intents/' . Request::i()->payment_intent, null, 'GET' );
if( $intent['client_secret'] != Request::i()->payment_intent_client_secret )
{
throw new \Exception;
throw new Exception;
}
}
catch ( \Exception $e )
catch (Exception )
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=nexus&module=checkout&controller=checkout&do=transaction&id=&t=" . \IPS\Request::i()->nexusTransactionId, 'front', 'nexus_checkout', \IPS\Settings::i()->nexus_https ) );
Output::i()->redirect( Url::internal( "app=nexus&module=checkout&controller=checkout&do=transaction&id=&t=" . Request::i()->nexusTransactionId, 'front', 'nexus_checkout', Settings::i()->nexus_https ) );
}
/* If we're a guest, but the transaction belongs to a member, that's because the webhook has
processed the transaction and created an account - so we need to log the newly created
member in. This is okay to do because we've checked client_secret is correct, meaning
we know this is a genuine redirect back from Stripe after payment of this transaction */
if ( !\IPS\Member::loggedIn()->member_id and $transaction->member->member_id )
if ( !Member::loggedIn()->member_id and $transaction->member->member_id )
{
\IPS\Session::i()->setMember( $transaction->member );
\IPS\Member\Device::loadOrCreate( $transaction->member, FALSE )->updateAfterAuthentication( NULL );
Session::i()->setMember( $transaction->member );
Device::loadOrCreate( $transaction->member, FALSE )->updateAfterAuthentication( NULL );
}
/* And then send them on */
switch( $intent['status'] )
{
case 'processing':
\IPS\Output::i()->redirect( $transaction->url()->setQueryString( 'pending', 1 ) );
Output::i()->redirect( $transaction->url()->setQueryString( 'pending', 1 ) );
break;
case 'succeeded':
\IPS\Output::i()->redirect( $transaction->url() );
Output::i()->redirect( $transaction->url() );
break;
default:
@@ -67,6 +77,6 @@ switch( $intent['status'] )
{
$url = $url->setQueryString( 'err', $intent['last_payment_error']['message'] );
}
\IPS\Output::i()->redirect( $url );
Output::i()->redirect( $url );
break;
}
+140 -127
View File
@@ -9,11 +9,27 @@
* @since 20 Jul 2017
*/
\define('REPORT_EXCEPTIONS', TRUE);
require_once '../../../../init.php';
\IPS\Session\Front::i();
use IPS\core\AdminNotification;
use IPS\Db;
use IPS\Http\Url;
use IPS\Math\Number;
use IPS\Member;
use IPS\nexus\Customer;
use IPS\nexus\Fraud\MaxMind\Request;
use IPS\nexus\Gateway;
use IPS\nexus\Gateway\Stripe;
use IPS\nexus\Invoice;
use IPS\nexus\Money;
use IPS\nexus\Transaction;
use IPS\Output;
use IPS\Session\Front;
use IPS\Settings;
\IPS\Output::setCacheTime( false );
define('REPORT_EXCEPTIONS', TRUE);
require_once '../../../../init.php';
Front::i();
Output::i()->pageCaching = FALSE;
/**
* Stripe Webhook Handler
@@ -23,17 +39,17 @@ class stripeWebhookHandler
/**
* @brief Raw Webhook Data (as a string)
*/
private $body;
private mixed $body;
/**
* @brief Parsed Webhook Data (as an array)
*/
private $data;
private array $data;
/**
* @brief Transaction
*/
private $transaction;
private Transaction $transaction;
/**
* Constructor
@@ -41,7 +57,7 @@ class stripeWebhookHandler
* @param string $body The raw body posted to this script
* @return void
*/
public function __construct( $body )
public function __construct( string $body )
{
$this->body = $body;
$this->data = json_decode( $body, TRUE );
@@ -52,24 +68,24 @@ class stripeWebhookHandler
*
* @return string
*/
private function sourceChargeable()
private function sourceChargeable() : string
{
/* Don't need to do anything for cards, since this is only for asynchronous payments */
if ( isset( $this->data['data']['object']['type'] ) and \in_array( $this->data['data']['object']['type'], array( 'card' ) ) )
if ( isset( $this->data['data']['object']['type'] ) and in_array( $this->data['data']['object']['type'], array( 'card' ) ) )
{
return 'NO_PROCESSING_REQUIRED';
}
/* Check the status */
if ( !\in_array( $this->transaction->status, array( \IPS\nexus\Transaction::STATUS_PENDING, \IPS\nexus\Transaction::STATUS_WAITING ) ) )
if ( !in_array( $this->transaction->status, array( Transaction::STATUS_PENDING, Transaction::STATUS_WAITING ) ) )
{
if ( \in_array( $this->transaction->status, array( \IPS\nexus\Transaction::STATUS_GATEWAY_PENDING, \IPS\nexus\Transaction::STATUS_PAID ) ) )
if ( in_array( $this->transaction->status, array( Transaction::STATUS_GATEWAY_PENDING, Transaction::STATUS_PAID ) ) )
{
return 'ALREADY_PROCESSED';
}
else
{
throw new \Exception('BAD_STATUS');
throw new Exception('BAD_STATUS');
}
}
@@ -77,19 +93,19 @@ class stripeWebhookHandler
$source = $this->transaction->method->api( 'sources/' . preg_replace( '/[^A-Z0-9_]/i', '', $this->data['data']['object']['id'] ), null, 'get' );
if ( $source['client_secret'] != $this->data['data']['object']['client_secret'] )
{
throw new \Exception('BAD_SECRET');
throw new Exception('BAD_SECRET');
}
/* Check we're not just going to refuse this */
$maxMind = NULL;
if ( \IPS\Settings::i()->maxmind_key and ( !\IPS\Settings::i()->maxmind_gateways or \IPS\Settings::i()->maxmind_gateways == '*' or \in_array( $this->transaction->method->id, explode( ',', \IPS\Settings::i()->maxmind_gateways ) ) ) )
if ( Settings::i()->maxmind_key and ( !Settings::i()->maxmind_gateways or Settings::i()->maxmind_gateways == '*' or in_array( $this->transaction->method->id, explode( ',', Settings::i()->maxmind_gateways ) ) ) )
{
$maxMind = new \IPS\nexus\Fraud\MaxMind\Request( FALSE );
$maxMind = new Request( FALSE );
$maxMind->setIpAddress( $this->transaction->ip );
$maxMind->setTransaction( $this->transaction );
}
$fraudResult = $this->transaction->runFraudCheck( $maxMind );
if ( $fraudResult === \IPS\nexus\Transaction::STATUS_REFUSED )
if ( $fraudResult === Transaction::STATUS_REFUSED )
{
$this->transaction->executeFraudAction( $fraudResult, FALSE );
$this->transaction->sendNotification();
@@ -99,7 +115,7 @@ class stripeWebhookHandler
else
{
$this->transaction->auth = $this->transaction->method->auth( $this->transaction, array( $this->transaction->method->id . '_card' => $source['id'] ) );
$this->transaction->status = \IPS\nexus\Transaction::STATUS_GATEWAY_PENDING;
$this->transaction->status = Transaction::STATUS_GATEWAY_PENDING;
$this->transaction->save();
}
@@ -112,18 +128,18 @@ class stripeWebhookHandler
*
* @return string
*/
private function chargeSucceeded()
private function chargeSucceeded() : string
{
/* Check the status */
if ( !\in_array( $this->transaction->status, [ \IPS\nexus\Transaction::STATUS_GATEWAY_PENDING, IPS\nexus\Transaction::STATUS_PENDING ] ) )
if ( !in_array( $this->transaction->status, [ Transaction::STATUS_GATEWAY_PENDING, IPS\nexus\Transaction::STATUS_PENDING ] ) )
{
if ( $this->transaction->status === \IPS\nexus\Transaction::STATUS_PAID )
if ( $this->transaction->status === Transaction::STATUS_PAID )
{
return 'ALREADY_PROCESSED';
}
else
{
throw new \Exception('BAD_STATUS');
throw new Exception('BAD_STATUS');
}
}
@@ -131,21 +147,21 @@ class stripeWebhookHandler
try
{
$charge = $this->transaction->method->api( 'charges/' . preg_replace( '/[^A-Z0-9_]/i', '', $this->data['data']['object']['id'] ), null, 'get' );
if ( !\in_array( $charge['status'], array( 'succeeded', 'paid' ) ) )
if ( !in_array( $charge['status'], array( 'succeeded', 'paid' ) ) )
{
throw new \Exception;
throw new Exception;
}
}
catch ( \Exception $e )
catch (Exception )
{
throw new \Exception('INVALID_CHARGE');
throw new Exception('INVALID_CHARGE');
}
/* Create a MaxMind request */
$maxMind = NULL;
if ( \IPS\Settings::i()->maxmind_key and ( !\IPS\Settings::i()->maxmind_gateways or \IPS\Settings::i()->maxmind_gateways == '*' or \in_array( $this->transaction->method->id, explode( ',', \IPS\Settings::i()->maxmind_gateways ) ) ) )
if ( Settings::i()->maxmind_key and ( !Settings::i()->maxmind_gateways or Settings::i()->maxmind_gateways == '*' or in_array( $this->transaction->method->id, explode( ',', Settings::i()->maxmind_gateways ) ) ) )
{
$maxMind = new \IPS\nexus\Fraud\MaxMind\Request( FALSE );
$maxMind = new Request( FALSE );
$maxMind->setIpAddress( $this->transaction->ip );
$maxMind->setTransaction( $this->transaction );
}
@@ -158,11 +174,11 @@ class stripeWebhookHandler
}
/* If we're not being fraud blocked, we can approve */
if ( $fraudResult === \IPS\nexus\Transaction::STATUS_PAID )
if ( $fraudResult === Transaction::STATUS_PAID )
{
$this->transaction->member->log( 'transaction', array(
'type' => 'paid',
'status' => \IPS\nexus\Transaction::STATUS_PAID,
'status' => Transaction::STATUS_PAID,
'id' => $this->transaction->id,
'invoice_id' => $this->transaction->invoice->id,
'invoice_title' => $this->transaction->invoice->title,
@@ -182,39 +198,39 @@ class stripeWebhookHandler
*
* @return string
*/
private function chargeSucceededNew()
private function chargeSucceededNew() : string
{
/* Check the status */
if ( !\in_array( $this->transaction->status, [ \IPS\nexus\Transaction::STATUS_GATEWAY_PENDING, IPS\nexus\Transaction::STATUS_PENDING ] ) )
if ( !in_array( $this->transaction->status, [ Transaction::STATUS_GATEWAY_PENDING, IPS\nexus\Transaction::STATUS_PENDING ] ) )
{
if ( $this->transaction->status === \IPS\nexus\Transaction::STATUS_PAID )
if ( $this->transaction->status === Transaction::STATUS_PAID )
{
return 'ALREADY_PROCESSED';
}
else
{
throw new \Exception('BAD_STATUS');
throw new Exception('BAD_STATUS');
}
}
/* If the invoice this is for is already paid, this may be a duplicate charge, so don't capture */
if ( $this->transaction->invoice->status === \IPS\nexus\Invoice::STATUS_PAID )
if ( $this->transaction->invoice->status === Invoice::STATUS_PAID )
{
throw new \Exception('INVOICE_ALREADY_PAID');
throw new Exception('INVOICE_ALREADY_PAID');
}
/* Validate the charge with Stripe */
try
{
$charge = $this->transaction->method->api( 'charges/' . preg_replace( '/[^A-Z0-9_]/i', '', $this->data['data']['object']['id'] ), null, 'get' );
if ( !\in_array( $charge['status'], array( 'succeeded', 'paid' ) ) )
if ( !in_array( $charge['status'], array( 'succeeded', 'paid' ) ) )
{
throw new \Exception;
throw new Exception;
}
}
catch ( \Exception $e )
catch (Exception )
{
throw new \Exception('INVALID_CHARGE');
throw new Exception('INVALID_CHARGE');
}
/* If this was done by an admin, we just capture and approve straight away */
@@ -224,11 +240,11 @@ class stripeWebhookHandler
$this->transaction->capture();
$this->transaction->member->log( 'transaction', array(
'type' => 'paid',
'status' => \IPS\nexus\Transaction::STATUS_PAID,
'status' => Transaction::STATUS_PAID,
'id' => $this->transaction->id,
'invoice_id' => $this->transaction->invoice->id,
'invoice_title' => $this->transaction->invoice->title,
), \IPS\Member::load( $this->data['data']['object']['metadata']['Admin'] ) );
), Member::load( $this->data['data']['object']['metadata']['Admin'] ) );
$this->transaction->approve();
}
@@ -237,15 +253,15 @@ class stripeWebhookHandler
{
/* Create a MaxMind request */
$maxMind = NULL;
if ( \IPS\Settings::i()->maxmind_key and ( !\IPS\Settings::i()->maxmind_gateways or \IPS\Settings::i()->maxmind_gateways == '*' or \in_array( $this->transaction->method->id, explode( ',', \IPS\Settings::i()->maxmind_gateways ) ) ) )
if ( Settings::i()->maxmind_key and ( !Settings::i()->maxmind_gateways or Settings::i()->maxmind_gateways == '*' or in_array( $this->transaction->method->id, explode( ',', Settings::i()->maxmind_gateways ) ) ) )
{
$maxMind = new \IPS\nexus\Fraud\MaxMind\Request( FALSE );
$maxMind = new Request( FALSE );
$maxMind->setIpAddress( $this->transaction->ip );
$maxMind->setTransaction( $this->transaction );
}
/* Set the authorize date (it's already actually been authorized) */
$this->transaction->auth = \IPS\DateTime::ts( $this->data['data']['object']['created'] )->add( new \DateInterval( 'P7D' ) );
$this->transaction->auth = \IPS\DateTime::ts( $this->data['data']['object']['created'] )->add( new DateInterval( 'P7D' ) );
/* Check Fraud Rules and capture */
$this->transaction->checkFraudRulesAndCapture( $maxMind );
@@ -263,18 +279,18 @@ class stripeWebhookHandler
*
* @return string
*/
private function chargeFailed()
private function chargeFailed() : string
{
/* Check the status */
if ( !\in_array( $this->transaction->status, [ \IPS\nexus\Transaction::STATUS_GATEWAY_PENDING, IPS\nexus\Transaction::STATUS_PENDING ] ) )
if ( !in_array( $this->transaction->status, [ Transaction::STATUS_GATEWAY_PENDING, IPS\nexus\Transaction::STATUS_PENDING ] ) )
{
if ( $this->transaction->status === \IPS\nexus\Transaction::STATUS_REFUSED )
if ( $this->transaction->status === Transaction::STATUS_REFUSED )
{
return 'ALREADY_PROCESSED';
}
else
{
throw new \Exception('BAD_STATUS');
throw new Exception('BAD_STATUS');
}
}
@@ -284,23 +300,23 @@ class stripeWebhookHandler
$charge = $this->transaction->method->api( 'charges/' . preg_replace( '/[^A-Z0-9_]/i', '', $this->data['data']['object']['id'] ), null, 'get' );
if ( $charge['status'] !== 'failed' )
{
throw new \Exception;
throw new Exception;
}
}
catch ( \Exception $e )
catch (Exception )
{
throw new \Exception('INVALID_CHARGE');
throw new Exception('INVALID_CHARGE');
}
/* Mark it failed */
$this->transaction->status = \IPS\nexus\Transaction::STATUS_REFUSED;
$this->transaction->status = Transaction::STATUS_REFUSED;
$extra = $this->transaction->extra;
$extra['history'][] = array( 's' => \IPS\nexus\Transaction::STATUS_REFUSED, 'noteRaw' => $this->data['data']['object']['failure_message'] );
$extra['history'][] = array( 's' => Transaction::STATUS_REFUSED, 'noteRaw' => $this->data['data']['object']['failure_message'] );
$this->transaction->extra = $extra;
$this->transaction->save();
$this->transaction->member->log( 'transaction', array(
'type' => 'paid',
'status' => \IPS\nexus\Transaction::STATUS_REFUSED,
'status' => Transaction::STATUS_REFUSED,
'id' => $this->transaction->id,
'invoice_id' => $this->transaction->invoice->id,
'invoice_title' => $this->transaction->title,
@@ -321,15 +337,15 @@ class stripeWebhookHandler
private function paymentIntentFailed(): string
{
/* Check the status */
if ( !\in_array( $this->transaction->status, [ \IPS\nexus\Transaction::STATUS_GATEWAY_PENDING, IPS\nexus\Transaction::STATUS_PENDING ] ) )
if ( !in_array( $this->transaction->status, [ Transaction::STATUS_GATEWAY_PENDING, Transaction::STATUS_PENDING ] ) )
{
if ( $this->transaction->status === \IPS\nexus\Transaction::STATUS_REFUSED )
if ( $this->transaction->status === Transaction::STATUS_REFUSED )
{
return 'ALREADY_PROCESSED';
}
else
{
throw new \Exception('BAD_STATUS');
throw new Exception('BAD_STATUS');
}
}
@@ -339,23 +355,23 @@ class stripeWebhookHandler
$pi = $this->transaction->method->api( 'payment_intents/' . $this->data['data']['object']['id'], null, 'get' );
if ( !is_array( $pi['last_payment_error'] ) )
{
throw new \Exception;
throw new Exception;
}
}
catch ( \Exception $e )
catch ( Exception $e )
{
throw new \Exception('INVALID_PI');
throw new Exception('INVALID_PI');
}
/* Mark it failed */
$this->transaction->status = \IPS\nexus\Transaction::STATUS_REFUSED;
$this->transaction->status = Transaction::STATUS_REFUSED;
$extra = $this->transaction->extra;
$extra['history'][] = array( 's' => \IPS\nexus\Transaction::STATUS_REFUSED, 'noteRaw' => $this->data['data']['object']['last_payment_error']['message'] );
$extra['history'][] = array( 's' => Transaction::STATUS_REFUSED, 'noteRaw' => $this->data['data']['object']['last_payment_error']['message'] );
$this->transaction->extra = $extra;
$this->transaction->save();
$this->transaction->member->log( 'transaction', array(
'type' => 'paid',
'status' => \IPS\nexus\Transaction::STATUS_REFUSED,
'status' => Transaction::STATUS_REFUSED,
'id' => $this->transaction->id,
'invoice_id' => $this->transaction->invoice->id,
'invoice_title' => $this->transaction->title,
@@ -367,24 +383,24 @@ class stripeWebhookHandler
/* Return */
return 'OK';
}
/**
* A chargeback/dispute has been made against a transaction so we need to mark it as such locally
*
* @return string
*/
private function disputeCreated()
private function disputeCreated() : string
{
/* Validate the dispute with Stripe */
try
{
$dispute = $this->transaction->method->api( 'disputes/' . preg_replace( '/[^A-Z0-9_]/i', '', $this->data['data']['object']['id'] ), null, 'get' );
if ( !\in_array( $dispute['status'], array( 'needs_response', 'warning_needs_response' ) ) )
if ( !in_array( $dispute['status'], array( 'needs_response', 'warning_needs_response' ) ) )
{
throw new \Exception;
throw new Exception;
}
if ( \substr( $this->transaction->gw_id, 0, 3 ) === 'pi_' )
if ( substr( $this->transaction->gw_id, 0, 3 ) === 'pi_' )
{
$paymentIntent = $this->transaction->method->api( 'charges?payment_intent=' . $this->transaction->gw_id, null, 'get' );
@@ -400,23 +416,23 @@ class stripeWebhookHandler
if ( !$ok )
{
throw new \Exception;
throw new Exception;
}
}
elseif ( $dispute['charge'] !== $this->transaction->gw_id )
{
throw new \Exception;
throw new Exception;
}
}
catch ( \Exception $e )
catch (Exception )
{
throw new \Exception('INVALID_DISPUTE');
throw new Exception('INVALID_DISPUTE');
}
/* Mark the transaction as disputed */
$this->transaction->status = \IPS\nexus\Transaction::STATUS_DISPUTED;
$this->transaction->status = Transaction::STATUS_DISPUTED;
$extra = $this->transaction->extra;
$extra['history'][] = array( 's' => \IPS\nexus\Transaction::STATUS_DISPUTED, 'on' => $this->data['data']['object']['created'], 'ref' => $this->data['data']['object']['id'] );
$extra['history'][] = array( 's' => Transaction::STATUS_DISPUTED, 'on' => $this->data['data']['object']['created'], 'ref' => $this->data['data']['object']['id'] );
$this->transaction->extra = $extra;
$this->transaction->save();
@@ -425,16 +441,16 @@ class stripeWebhookHandler
{
$this->transaction->member->log( 'transaction', array(
'type' => 'status',
'status' => \IPS\nexus\Transaction::STATUS_DISPUTED,
'status' => Transaction::STATUS_DISPUTED,
'id' => $this->transaction->id
) );
}
/* Mark the invoice as not paid (revoking benefits) */
$this->transaction->invoice->markUnpaid( \IPS\nexus\Invoice::STATUS_CANCELED );
$this->transaction->invoice->markUnpaid( Invoice::STATUS_CANCELED );
/* Send admin notification */
\IPS\core\AdminNotification::send( 'nexus', 'Transaction', \IPS\nexus\Transaction::STATUS_DISPUTED, TRUE, $this->transaction );
AdminNotification::send( 'nexus', 'Transaction', Transaction::STATUS_DISPUTED, TRUE, $this->transaction );
/* Return */
return 'OK';
@@ -445,13 +461,13 @@ class stripeWebhookHandler
*
* @return string
*/
private function disputeClosed()
private function disputeClosed() : string
{
/* Validate the dispute with Stripe */
try
{
$dispute = $this->transaction->method->api( 'disputes/' . preg_replace( '/[^A-Z0-9_]/i', '', $this->data['data']['object']['id'] ) );
if ( \substr( $this->transaction->gw_id, 0, 3 ) === 'pi_' )
if ( substr( $this->transaction->gw_id, 0, 3 ) === 'pi_' )
{
$paymentIntent = $this->transaction->method->api( 'charges?payment_intent=' . $this->transaction->gw_id, null, 'get' );
@@ -467,27 +483,27 @@ class stripeWebhookHandler
if ( !$ok )
{
throw new \Exception;
throw new Exception;
}
}
elseif ( $dispute['charge'] !== $this->transaction->gw_id )
{
throw new \Exception;
throw new Exception;
}
}
catch ( \Exception $e )
catch (Exception )
{
throw new \Exception('INVALID_DISPUTE');
throw new Exception('INVALID_DISPUTE');
}
/* Did we win or lose? */
if ( \in_array( $dispute['status'], array( 'won', 'warning_closed' ) ) )
if ( in_array( $dispute['status'], array( 'won', 'warning_closed' ) ) )
{
// Do nothing
}
elseif ( \in_array( $dispute['status'], array( 'lost' ) ) )
elseif ( in_array( $dispute['status'], array( 'lost' ) ) )
{
$this->transaction->status = \IPS\nexus\Transaction::STATUS_REFUNDED;
$this->transaction->status = Transaction::STATUS_REFUNDED;
$this->transaction->save();
}
@@ -499,12 +515,12 @@ class stripeWebhookHandler
* Run
*
* @param string $signature The signature provided by this request
* @return void
* @return string
*/
public function run( $signature )
public function run( string $signature ) : string
{
/* Ignore anything we don't care about */
if ( !isset( $this->data['type'] ) or !\in_array( $this->data['type'], array( 'source.chargeable', 'charge.succeeded', 'charge.failed', 'charge.dispute.created', 'charge.dispute.closed'/*, 'payment_intent.payment_failed'*/ ) ) )
if ( !isset( $this->data['type'] ) or !in_array( $this->data['type'], array( 'source.chargeable', 'charge.succeeded', 'charge.failed', 'charge.dispute.created', 'charge.dispute.closed', 'payment_intent.payment_failed' ) ) )
{
return 'UNNEEDED_TYPE';
}
@@ -515,21 +531,19 @@ class stripeWebhookHandler
{
if ( isset( $this->data['data']['object']['metadata']['Transaction ID'] ) )
{
/* @note SELECT_FROM_WRITE_SERVER transaction race condition */
$this->transaction = \IPS\nexus\Transaction::constructFromData( \IPS\Db::i()->select( '*', 'nexus_transactions', array( 't_id=?', $this->data['data']['object']['metadata']['Transaction ID'] ), flags: \IPS\Db::SELECT_FROM_WRITE_SERVER )->first() );
$this->transaction = Transaction::constructFromData( Db::i()->select( '*', 'nexus_transactions', array( 't_id=?', $this->data['data']['object']['metadata']['Transaction ID'] ), flags: Db::SELECT_FROM_WRITE_SERVER )->first() );
}
elseif ( isset( $this->data['data']['object']['redirect']['return_url'] ) )
{
$url = new \IPS\Http\Url( $this->data['data']['object']['redirect']['return_url'] );
/* @note SELECT_FROM_WRITE_SERVER transaction race condition */
$this->transaction = \IPS\nexus\Transaction::constructFromData( \IPS\Db::i()->select( '*', 'nexus_transactions', array( 't_id=?', $url->queryString['nexusTransactionId'] ), flags: \IPS\Db::SELECT_FROM_WRITE_SERVER )->first() );
$url = new Url( $this->data['data']['object']['redirect']['return_url'] );
$this->transaction = Transaction::constructFromData( Db::i()->select( '*', 'nexus_transactions', array( 't_id=?', $url->queryString['nexusTransactionId'] ), flags: Db::SELECT_FROM_WRITE_SERVER )->first() );
}
elseif ( isset( $this->data['data']['object']['charge'] ) )
{
$paymentIntentId = NULL;
foreach ( \IPS\nexus\Gateway::roots() as $method )
foreach ( Gateway::roots() as $method )
{
if ( $method instanceof \IPS\nexus\Gateway\Stripe )
if ( $method instanceof Stripe )
{
try
{
@@ -540,7 +554,7 @@ class stripeWebhookHandler
}
break;
}
catch ( \Exception $e )
catch (Exception )
{
// Do nothing - try the next one
}
@@ -549,13 +563,11 @@ class stripeWebhookHandler
if ( $paymentIntentId )
{
/* @note SELECT_FROM_WRITE_SERVER transaction race condition */
$this->transaction = \IPS\nexus\Transaction::constructFromData( \IPS\Db::i()->select( '*', 'nexus_transactions', array( '( t_gw_id=? OR t_gw_id=? )', $paymentIntentId, $this->data['data']['object']['charge'] ), flags: \IPS\Db::SELECT_FROM_WRITE_SERVER )->first() );
$this->transaction = Transaction::constructFromData( Db::i()->select( '*', 'nexus_transactions', array( '( t_gw_id=? OR t_gw_id=? )', $paymentIntentId, $this->data['data']['object']['charge'] ), flags: Db::SELECT_FROM_WRITE_SERVER )->first() );
}
else
{
/* @note SELECT_FROM_WRITE_SERVER transaction race condition */
$this->transaction = \IPS\nexus\Transaction::constructFromData( \IPS\Db::i()->select( '*', 'nexus_transactions', array( 't_gw_id=?', $this->data['data']['object']['charge'] ), flags: \IPS\Db::SELECT_FROM_WRITE_SERVER )->first() );
{
$this->transaction = Transaction::constructFromData( Db::i()->select( '*', 'nexus_transactions', array( 't_gw_id=?', $this->data['data']['object']['charge'] ), flags: Db::SELECT_FROM_WRITE_SERVER )->first() );
}
}
elseif ( isset( $this->data['data']['object']['id'] ) and mb_substr( $this->data['data']['object']['id'], 0, 4 ) !== 'src_' )
@@ -576,24 +588,23 @@ class stripeWebhookHandler
try
{
/* @note SELECT_FROM_WRITE_SERVER transaction race condition */
$this->transaction = \IPS\nexus\Transaction::constructFromData( \IPS\Db::i()->select( '*', 'nexus_transactions', $where, flags: \IPS\Db::SELECT_FROM_WRITE_SERVER )->first() );
$this->transaction = Transaction::constructFromData( Db::i()->select( '*', 'nexus_transactions', $where, flags: Db::SELECT_FROM_WRITE_SERVER )->first() );
}
catch ( \UnderflowException $e )
catch (UnderflowException $e )
{
/* If we can't find a transaction, but there is an "IP Address" in the metadata, this is from the actual checkout process: create a transaction */
if ( isset( $this->data['data']['object']['metadata']['Invoice ID'] ) and isset( $this->data['data']['object']['metadata']['IP Address'] ) and isset( $this->data['data']['object']['metadata']['Payment Method ID'] ) )
{
$invoice = \IPS\nexus\Invoice::load( \intval( $this->data['data']['object']['metadata']['Invoice ID'] ) );
$invoice = Invoice::load( intval( $this->data['data']['object']['metadata']['Invoice ID'] ) );
$this->transaction = new \IPS\nexus\Transaction;
$this->transaction->member = isset( $this->data['data']['object']['metadata']['Customer ID'] ) ? \IPS\nexus\Customer::load( \intval( $this->data['data']['object']['metadata']['Customer ID'] ) ) : $invoice->member;
$this->transaction = new Transaction;
$this->transaction->member = isset( $this->data['data']['object']['metadata']['Customer ID'] ) ? Customer::load( intval( $this->data['data']['object']['metadata']['Customer ID'] ) ) : $invoice->member;
$this->transaction->invoice = $invoice;
$this->transaction->amount = new \IPS\nexus\Money( new \IPS\Math\Number( mb_substr( $this->data['data']['object']['amount'], 0, -2 ) . '.' . mb_substr( $this->data['data']['object']['amount'], -2 ) ), mb_strtoupper( $this->data['data']['object']['currency'] ) );
$this->transaction->amount = new Money( new Number( mb_substr( $this->data['data']['object']['amount'], 0, -2 ) . '.' . mb_substr( $this->data['data']['object']['amount'], -2 ) ), mb_strtoupper( $this->data['data']['object']['currency'] ) );
$this->transaction->ip = $this->data['data']['object']['metadata']['IP Address'];
$this->transaction->gw_id = isset( $this->data['data']['object']['payment_intent'] ) ? $this->data['data']['object']['payment_intent'] : $this->data['data']['object']['id'];
$this->transaction->status = \IPS\nexus\Transaction::STATUS_PENDING;
$this->transaction->method = \IPS\nexus\Gateway::load( \intval( $this->data['data']['object']['metadata']['Payment Method ID'] ) );
$this->transaction->gw_id = $this->data['data']['object']['payment_intent'] ?? $this->data['data']['object']['id'];
$this->transaction->status = Transaction::STATUS_PENDING;
$this->transaction->method = Gateway::load( intval( $this->data['data']['object']['metadata']['Payment Method ID'] ) );
$this->transaction->save();
$new = TRUE;
@@ -609,20 +620,20 @@ class stripeWebhookHandler
return 'NO_TRANSACTION_INFORMATION';
}
}
catch ( \Exception $e )
catch (Exception )
{
if ( $this->data['type'] === 'charge.failed' )
{
return 'COULD_NOT_FIND_TRANSACTION'; // Sometimes if a transaction fails we don't create a transaction record
}
throw new \Exception('COULD_NOT_FIND_TRANSACTION');
throw new Exception('COULD_NOT_FIND_TRANSACTION');
}
/* Validate the signature */
if ( !( $this->transaction->method instanceof \IPS\nexus\Gateway\Stripe ) )
if ( !( $this->transaction->method instanceof Stripe ) )
{
throw new \Exception('INVALID_GATEWAY');
throw new Exception('INVALID_GATEWAY');
}
$settings = json_decode( $this->transaction->method->settings, TRUE );
if ( isset( $settings['webhook_secret'] ) and $settings['webhook_secret'] ) // In case they upgraded and haven't provided one
@@ -630,7 +641,7 @@ class stripeWebhookHandler
$sig = array();
foreach ( explode( ',', $signature ) as $row )
{
if ( \strpos( $row, '=' ) !== FALSE )
if ( strpos( $row, '=' ) !== FALSE )
{
list( $k, $v ) = explode( '=', trim( $row ) );
$sig[ trim( $k ) ][] = trim( $v );
@@ -642,14 +653,14 @@ class stripeWebhookHandler
$signedPayload = $sig['t'][0] . '.' . $this->body;
$signature = hash_hmac( 'sha256', $signedPayload, $settings['webhook_secret'] );
if ( !\in_array( $signature, $sig['v1'] ) )
if ( !in_array( $signature, $sig['v1'] ) )
{
throw new \Exception('INVALID_SIGNING_SECRET');
throw new Exception('INVALID_SIGNING_SECRET');
}
}
else
{
throw new \Exception('INVALID_SIGNING_SECRET');
throw new Exception('INVALID_SIGNING_SECRET');
}
}
@@ -668,12 +679,14 @@ class stripeWebhookHandler
return $this->disputeCreated();
case 'charge.dispute.closed':
return $this->disputeClosed();
/*case 'payment_intent.payment_failed':
return $this->paymentIntentFailed();*/
case 'payment_intent.payment_failed':
return $this->paymentIntentFailed();
default:
return 'UNNEEDED_TYPE';
}
}
return '';
}
}
@@ -681,10 +694,10 @@ $class = new stripeWebhookHandler( trim( @file_get_contents('php://input') ) );
try
{
sleep( 5 ); // Stripe can send web hooks very quickly, we need to delay to ensure we've processed the transaction first.
$response = $class->run( isset( $_SERVER['HTTP_STRIPE_SIGNATURE'] ) ? $_SERVER['HTTP_STRIPE_SIGNATURE'] : '' );
\IPS\Output::i()->sendOutput( $response, 200, 'text/plain' );
$response = $class->run($_SERVER['HTTP_STRIPE_SIGNATURE'] ?? '');
Output::i()->sendOutput( $response, 200, 'text/plain' );
}
catch ( \Exception $e )
catch (Exception $e)
{
\IPS\Output::i()->sendOutput( $e->getMessage(), 500, 'text/plain' );
Output::i()->sendOutput( $e->getMessage(), 500, 'text/plain' );
}
+68 -53
View File
@@ -9,19 +9,29 @@
* @since 01 Apr 2015
*/
\define('REPORT_EXCEPTIONS', TRUE);
require_once '../../../../init.php';
\IPS\Dispatcher\External::i();
use IPS\Dispatcher\External;
use IPS\nexus\Customer;
use IPS\nexus\Purchase\LicenseKey;
use IPS\Output;
use IPS\Request;
use const IPS\NEXUS_LKEY_API_ALLOW_IP_OVERRIDE;
use const IPS\NEXUS_LKEY_API_CHECK_IP;
use const IPS\NEXUS_LKEY_API_DISABLE;
if ( \IPS\NEXUS_LKEY_API_ALLOW_IP_OVERRIDE )
define('REPORT_EXCEPTIONS', TRUE);
require_once '../../../../init.php';
External::i();
if ( NEXUS_LKEY_API_ALLOW_IP_OVERRIDE )
{
$_SERVER['REMOTE_ADDR'] = isset( \IPS\Request::i()->ip ) ? \IPS\Request::i()->ip : $_SERVER['REMOTE_ADDR'];
$_SERVER['REMOTE_ADDR'] = isset( Request::i()->ip ) ? Request::i()->ip : $_SERVER['REMOTE_ADDR'];
}
/**
* API Exception
*/
class ApiException extends \Exception { }
class ApiException extends Exception
{ }
/**
* API class to verify license keys
@@ -35,30 +45,31 @@ class Api
* @param string $message Status message
* @return void
*/
public function error( $code, $message )
public function error( int $code, string $message ) : void
{
\IPS\Output::setCacheTime( false );
\IPS\Output::i()->sendOutput( json_encode( array( 'errorCode' => $code, 'errorMessage' => $message ) ), 400, 'application/json' );
Output::i()->pageCaching = FALSE;
Output::i()->sendOutput( json_encode( array( 'errorCode' => $code, 'errorMessage' => $message ) ), 400, 'application/json' );
}
/**
* Get key
*
* @param bool $validateIdentifier Should the identifier be validated?
* @return \IPS\nexus\Purchase\LicenseKey
* @return LicenseKey
* @throws ApiException
*/
protected function getKey( $validateIdentifier=TRUE )
protected function getKey( bool $validateIdentifier=TRUE ) : LicenseKey
{
try
{
$key = \IPS\nexus\Purchase\LicenseKey::load( isset( \IPS\Request::i()->key ) ? \IPS\Request::i()->key : NULL );
$key = LicenseKey::load( isset( Request::i()->key ) ? Request::i()->key : NULL );
if ( $key->key !== \IPS\Request::i()->key )
if ( $key->key !== Request::i()->key )
{
throw new ApiException( 'BAD_KEY_OR_ID', 105 );
}
if ( $validateIdentifier and $identifier = $this->getIdentifier( $key ) and ( !isset( \IPS\Request::i()->identifier ) or $identifier != \IPS\Request::i()->identifier ) )
if ( $validateIdentifier and $identifier = $this->getIdentifier( $key ) and ( !isset( Request::i()->identifier ) or $identifier != Request::i()->identifier ) )
{
throw new ApiException( 'BAD_KEY_OR_ID', 101 );
}
@@ -78,7 +89,7 @@ class Api
return $key;
}
catch ( \OutOfRangeException $e )
catch (OutOfRangeException )
{
throw new ApiException( 'BAD_KEY_OR_ID', 101 );
}
@@ -87,24 +98,24 @@ class Api
/**
* Get identifier
*
* @param \IPS\nexus\Purchase\LicenseKey $key The license key
* @param LicenseKey $key The license key
* @return string|NULL
*/
protected function getIdentifier( \IPS\nexus\Purchase\LicenseKey $key )
protected function getIdentifier( LicenseKey $key ) : string|null
{
$identifier = NULL;
switch ( $key->identifier )
{
case 'name':
$identifier = \IPS\nexus\Customer::load( $key->member )->cm_name;
$identifier = Customer::load( $key->member )->cm_name;
break;
case 'email':
$identifier = \IPS\nexus\Customer::load( $key->member )->email;
$identifier = Customer::load( $key->member )->email;
break;
case 'username':
$identifier = \IPS\nexus\Customer::load( $key->member )->name;
$identifier = Customer::load( $key->member )->name;
break;
default:
@@ -122,8 +133,9 @@ class Api
* Activate
*
* @return void
* @throws ApiException
*/
public function activate()
public function activate() : void
{
$key = $this->getKey( FALSE );
@@ -133,19 +145,19 @@ class Api
}
$identifier = $this->getIdentifier( $key );
$providedIdentifier = isset( \IPS\Request::i()->identifier ) ? \IPS\Request::i()->identifier : NULL;
if ( isset( \IPS\Request::i()->setIdentifier ) and \IPS\Request::i()->setIdentifier )
$providedIdentifier = isset( Request::i()->identifier ) ? Request::i()->identifier : NULL;
if ( isset( Request::i()->setIdentifier ) and Request::i()->setIdentifier )
{
if ( $identifier != $providedIdentifier )
{
if ( $identifier or \in_array( $key->identifier, array( 'name', 'email', 'username' ) ) )
if ( $identifier or in_array( $key->identifier, array( 'name', 'email', 'username' ) ) )
{
throw new ApiException( 'BAD_KEY_OR_ID', 101 );
}
else
{
$cfields = $key->purchase->custom_fields;
$cfields[ $key->identifier ] = \IPS\Request::i()->setIdentifier;
$cfields[ $key->identifier ] = Request::i()->setIdentifier;
$key->purchase->custom_fields = $cfields;
$key->purchase->save();
}
@@ -156,9 +168,9 @@ class Api
throw new ApiException( 'BAD_KEY_OR_ID', 101 );
}
if ( \defined( 'NEXUS_LKEY_API_ALLOW_IP_OVERRIDE' ) )
if ( defined( 'NEXUS_LKEY_API_ALLOW_IP_OVERRIDE' ) )
{
$ip = $this->params['ip'] ? $this->params['ip'] : $_SERVER['REMOTE_ADDR'];
$ip = $this->params['ip'] ?: $_SERVER['REMOTE_ADDR'];
}
else
{
@@ -172,25 +184,26 @@ class Api
'activated' => time(),
'ip' => $ip,
'last_checked' => 0,
'extra' => isset( \IPS\Request::i()->extra ) ? json_decode( \IPS\Request::i()->extra ) : NULL,
'extra' => isset( Request::i()->extra ) ? json_decode( Request::i()->extra ) : NULL,
);
$key->activate_data = $activateData;
$key->uses++;
$key->save();
\IPS\nexus\Customer::load( $key->member )->log( 'lkey', array( 'type' => 'activated', 'key' => $key->key, 'ps_id' => $key->purchase->id ), FALSE );
Customer::load( $key->member )->log( 'lkey', array( 'type' => 'activated', 'key' => $key->key, 'ps_id' => $key->purchase->id ), FALSE );
\IPS\Output::setCacheTime( false );
\IPS\Output::i()->sendOutput( json_encode( array( 'response' => 'OKAY', 'usage_id' => $k ) ), 200, 'application/json' );
Output::i()->pageCaching = FALSE;
Output::i()->sendOutput( json_encode( array( 'response' => 'OKAY', 'usage_id' => $k ) ), 200, 'application/json' );
}
/**
* Check
*
* @return void
* @throws ApiException
*/
public function check()
public function check() : void
{
try
{
@@ -202,9 +215,9 @@ class Api
{
case 102:
case 103:
\IPS\Output::i()->sendOutput( json_encode( array( 'status' => 'INACTIVE' ) ), 200, 'application/json', \IPS\Output::getCacheHeaders( time(), 360 ) );
Output::i()->sendOutput( json_encode( array( 'status' => 'INACTIVE' ) ), 200, 'application/json', Output::getCacheHeaders( time(), 360 ) );
case 104:
\IPS\Output::i()->sendOutput( json_encode( array( 'status' => 'EXPIRED' ) ), 200, 'application/json', \IPS\Output::getCacheHeaders( time(), 360 ) );
Output::i()->sendOutput( json_encode( array( 'status' => 'EXPIRED' ) ), 200, 'application/json', Output::getCacheHeaders( time(), 360 ) );
default:
throw $e;
@@ -212,28 +225,29 @@ class Api
}
$activateData = $key->activate_data;
if ( !isset( \IPS\Request::i()->usage_id ) or !isset( $activateData[ \IPS\Request::i()->usage_id ] ) )
if ( !isset( Request::i()->usage_id ) or !isset( $activateData[ Request::i()->usage_id ] ) )
{
throw new ApiException( 'BAD_USAGE_ID', 303 );
}
if ( \IPS\NEXUS_LKEY_API_CHECK_IP and $activateData[ \IPS\Request::i()->usage_id ]['ip'] != $_SERVER['REMOTE_ADDR'] )
if ( NEXUS_LKEY_API_CHECK_IP and $activateData[ Request::i()->usage_id ]['ip'] != $_SERVER['REMOTE_ADDR'] )
{
throw new ApiException( 'BAD_IP', 304 );
}
$activateData[ \IPS\Request::i()->usage_id ]['last_checked'] = time();
$activateData[ Request::i()->usage_id ]['last_checked'] = time();
$key->activate_data = $activateData;
$key->save();
\IPS\Output::i()->sendOutput( json_encode( array( 'status' => 'ACTIVE', 'uses' => $key->uses, 'max_uses' => $key->max_uses ) ), 200, 'application/json', \IPS\Output::getCacheHeaders( time(), 360 ) );
Output::i()->sendOutput( json_encode( array( 'status' => 'ACTIVE', 'uses' => $key->uses, 'max_uses' => $key->max_uses ) ), 200, 'application/json', Output::getCacheHeaders( time(), 360 ) );
}
/**
* Get Information
*
* @return void
* @throws ApiException
*/
public function info()
public function info() : void
{
$key = $this->getKey();
@@ -253,7 +267,7 @@ class Api
);
}
\IPS\Output::i()->sendOutput( json_encode( array(
Output::i()->sendOutput( json_encode( array(
'key' => $key->key,
'identifier' => $this->getIdentifier( $key ),
'generated' => $key->generated->getTimestamp(),
@@ -266,47 +280,48 @@ class Api
'purchase_start' => $key->purchase->start->getTimestamp(),
'purchase_expire' => $key->purchase->expire ? $key->purchase->expire->getTimestamp() : NULL,
'purchase_children' => $children,
'customer_name' => \IPS\nexus\Customer::load( $key->member )->cm_name,
'customer_email' => \IPS\nexus\Customer::load( $key->member )->email,
'customer_name' => Customer::load( $key->member )->cm_name,
'customer_email' => Customer::load( $key->member )->email,
'uses' => $key->uses,
'max_uses' => $key->max_uses
) ), 200, 'application/json', \IPS\Output::getCacheHeaders( time(), 360 ) );
) ), 200, 'application/json', Output::getCacheHeaders( time(), 360 ) );
}
/**
* Update extra information
*
* @return void
* @throws ApiException
*/
public function updateExtra()
public function updateExtra() : void
{
$key = $this->getKey();
$activateData = $key->activate_data;
if ( !isset( \IPS\Request::i()->usage_id ) or !isset( $activateData[ \IPS\Request::i()->usage_id ] ) )
if ( !isset( Request::i()->usage_id ) or !isset( $activateData[ Request::i()->usage_id ] ) )
{
throw new ApiException( 'BAD_USAGE_ID', 303 );
}
if ( \IPS\NEXUS_LKEY_API_CHECK_IP and $activateData[ \IPS\Request::i()->usage_id ]['ip'] != $_SERVER['REMOTE_ADDR'] )
if ( NEXUS_LKEY_API_CHECK_IP and $activateData[ Request::i()->usage_id ]['ip'] != $_SERVER['REMOTE_ADDR'] )
{
throw new ApiException( 'BAD_IP', 304 );
}
$activateData[ \IPS\Request::i()->usage_id ]['extra'] = isset( \IPS\Request::i()->extra ) ? json_decode( \IPS\Request::i()->extra ) : NULL;
$activateData[ Request::i()->usage_id ]['extra'] = isset( Request::i()->extra ) ? json_decode( Request::i()->extra ) : NULL;
$key->activate_data = $activateData;
$key->save();
\IPS\Output::setCacheTime( false );
\IPS\Output::i()->sendOutput( json_encode( array( 'status' => 'OKAY' ) ), 200, 'application/json' );
Output::i()->pageCaching = FALSE;
Output::i()->sendOutput( json_encode( array( 'status' => 'OKAY' ) ), 200, 'application/json' );
}
}
$api = new api;
if ( !\IPS\NEXUS_LKEY_API_DISABLE )
if ( !NEXUS_LKEY_API_DISABLE )
{
foreach ( array( 'activate', 'check', 'info', 'updateExtra' ) as $k )
{
if ( isset( \IPS\Request::i()->$k ) )
if ( isset( Request::i()->$k ) )
{
try
{
@@ -316,7 +331,7 @@ if ( !\IPS\NEXUS_LKEY_API_DISABLE )
{
$api->error( $e->getMessage(), $e->getCode() );
}
catch ( Exception $e )
catch ( Exception )
{
$api->error( 0, 'INTERNAL_ERROR' );
}