Version 5.0.0 beta 1
This commit is contained in:
1 parent
25ddeb65d6
commit
15c7beabc5
6736 files changed
+627902
-497943
No files matched your search
@@ -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' );
|
||||
}
|
||||
Reference in new issue
Block a user