Version 4.7.23
This commit is contained in:
1 parent
7124a02564
commit
25ddeb65d6
1791 files changed
+76990
-44452
No files matched your search
@@ -12,7 +12,7 @@
|
||||
\define('REPORT_EXCEPTIONS', TRUE);
|
||||
require_once '../../../../init.php';
|
||||
|
||||
\IPS\Output::i()->pageCaching = FALSE;
|
||||
\IPS\Output::setCacheTime( false );
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
\define('REPORT_EXCEPTIONS', TRUE);
|
||||
require_once '../../../../init.php';
|
||||
|
||||
\IPS\Output::i()->pageCaching = FALSE;
|
||||
\IPS\Output::setCacheTime( false );
|
||||
|
||||
$webhookNotification = NULL;
|
||||
$transaction = NULL;
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
require_once '../../../../init.php';
|
||||
\IPS\Session\Front::i();
|
||||
|
||||
\IPS\Output::i()->pageCaching = FALSE;
|
||||
\IPS\Output::setCacheTime( false );
|
||||
|
||||
/**
|
||||
* PayPal Webhook Handler
|
||||
@@ -60,7 +60,8 @@ class paypalWebhookHandler
|
||||
/* Have we seen this transaction before? */
|
||||
try
|
||||
{
|
||||
$transaction = \IPS\Db::i()->select( '*', 'nexus_transactions', array( 't_gw_id=? AND t_method=?', $this->data['resource']['id'], $this->method->id ) )->first();
|
||||
/* @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();
|
||||
return 'ALREADY_PROCESSED';
|
||||
}
|
||||
catch ( \UnderflowException $e ) {}
|
||||
@@ -68,7 +69,8 @@ class paypalWebhookHandler
|
||||
/* Get the billing agreement */
|
||||
try
|
||||
{
|
||||
$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 ) )->first() );
|
||||
/* @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() );
|
||||
}
|
||||
catch ( \UnderflowException $e )
|
||||
{
|
||||
@@ -78,7 +80,8 @@ class paypalWebhookHandler
|
||||
/* Do it */
|
||||
try
|
||||
{
|
||||
$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 ) )->first() );
|
||||
/* @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->gw_id = $this->data['resource']['id'];
|
||||
$transaction->save();
|
||||
|
||||
|
||||
@@ -54,26 +54,48 @@ try
|
||||
/* Subscription */
|
||||
if ( isset( \IPS\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 */
|
||||
$response = $transaction->method->api( "billing/subscriptions/" . \IPS\Request::i()->subscription_id, NULL, 'get' );
|
||||
|
||||
/* Create Billing Agreement */
|
||||
try
|
||||
if( !isset( $billingAgreement ) )
|
||||
{
|
||||
$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 ) )->first() );
|
||||
$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();
|
||||
}
|
||||
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();
|
||||
|
||||
/* Check Fraud */
|
||||
$fraudResult = $fraudCheck( $transaction, FALSE );
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
require_once '../../../../init.php';
|
||||
\IPS\Session\Front::i();
|
||||
|
||||
\IPS\Output::i()->pageCaching = FALSE;
|
||||
\IPS\Output::setCacheTime( false );
|
||||
|
||||
/* Get the invoice */
|
||||
try
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
require_once '../../../../init.php';
|
||||
\IPS\Session\Front::i();
|
||||
|
||||
\IPS\Output::i()->pageCaching = FALSE;
|
||||
\IPS\Output::setCacheTime( 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 ) )
|
||||
@@ -29,8 +29,8 @@ sleep( 5 );
|
||||
try
|
||||
{
|
||||
$transaction = \IPS\nexus\Transaction::load( \IPS\Request::i()->nexusTransactionId );
|
||||
$source = $transaction->method->api( 'sources/' . preg_replace( '/[^A-Z0-9_]/i', '', \IPS\Request::i()->source ), NULL, 'get' );
|
||||
if ( $source['client_secret'] != \IPS\Request::i()->client_secret )
|
||||
$intent = $transaction->method->api( 'payment_intents/' . \IPS\Request::i()->payment_intent, null, 'GET' );
|
||||
if( $intent['client_secret'] != \IPS\Request::i()->payment_intent_client_secret )
|
||||
{
|
||||
throw new \Exception;
|
||||
}
|
||||
@@ -51,11 +51,22 @@ if ( !\IPS\Member::loggedIn()->member_id and $transaction->member->member_id )
|
||||
}
|
||||
|
||||
/* And then send them on */
|
||||
if ( $source['status'] === 'failed' )
|
||||
switch( $intent['status'] )
|
||||
{
|
||||
\IPS\Output::i()->redirect( $transaction->invoice->checkoutUrl() );
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Output::i()->redirect( $transaction->url()->setQueryString( 'pending', 1 ) );
|
||||
case 'processing':
|
||||
\IPS\Output::i()->redirect( $transaction->url()->setQueryString( 'pending', 1 ) );
|
||||
break;
|
||||
|
||||
case 'succeeded':
|
||||
\IPS\Output::i()->redirect( $transaction->url() );
|
||||
break;
|
||||
|
||||
default:
|
||||
$url = $transaction->invoice->checkoutUrl();
|
||||
if( isset( $intent['last_payment_error'] ) )
|
||||
{
|
||||
$url = $url->setQueryString( 'err', $intent['last_payment_error']['message'] );
|
||||
}
|
||||
\IPS\Output::i()->redirect( $url );
|
||||
break;
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
require_once '../../../../init.php';
|
||||
\IPS\Session\Front::i();
|
||||
|
||||
\IPS\Output::i()->pageCaching = FALSE;
|
||||
\IPS\Output::setCacheTime( false );
|
||||
|
||||
/**
|
||||
* Stripe Webhook Handler
|
||||
@@ -312,6 +312,61 @@ class stripeWebhookHandler
|
||||
/* Return */
|
||||
return 'OK';
|
||||
}
|
||||
|
||||
/**
|
||||
* A payment intent failed so we need to mark the transaction as failed locally
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
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 ( $this->transaction->status === \IPS\nexus\Transaction::STATUS_REFUSED )
|
||||
{
|
||||
return 'ALREADY_PROCESSED';
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new \Exception('BAD_STATUS');
|
||||
}
|
||||
}
|
||||
|
||||
/* Validate the charge with Stripe */
|
||||
try
|
||||
{
|
||||
$pi = $this->transaction->method->api( 'payment_intents/' . $this->data['data']['object']['id'], null, 'get' );
|
||||
if ( !is_array( $pi['last_payment_error'] ) )
|
||||
{
|
||||
throw new \Exception;
|
||||
}
|
||||
}
|
||||
catch ( \Exception $e )
|
||||
{
|
||||
throw new \Exception('INVALID_PI');
|
||||
}
|
||||
|
||||
/* Mark it failed */
|
||||
$this->transaction->status = \IPS\nexus\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'] );
|
||||
$this->transaction->extra = $extra;
|
||||
$this->transaction->save();
|
||||
$this->transaction->member->log( 'transaction', array(
|
||||
'type' => 'paid',
|
||||
'status' => \IPS\nexus\Transaction::STATUS_REFUSED,
|
||||
'id' => $this->transaction->id,
|
||||
'invoice_id' => $this->transaction->invoice->id,
|
||||
'invoice_title' => $this->transaction->title,
|
||||
), FALSE );
|
||||
|
||||
/* Send notification */
|
||||
$this->transaction->sendNotification();
|
||||
|
||||
/* Return */
|
||||
return 'OK';
|
||||
}
|
||||
|
||||
/**
|
||||
* A chargeback/dispute has been made against a transaction so we need to mark it as such locally
|
||||
@@ -449,7 +504,7 @@ class stripeWebhookHandler
|
||||
public function run( $signature )
|
||||
{
|
||||
/* 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' ) ) )
|
||||
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';
|
||||
}
|
||||
@@ -460,12 +515,14 @@ class stripeWebhookHandler
|
||||
{
|
||||
if ( isset( $this->data['data']['object']['metadata']['Transaction ID'] ) )
|
||||
{
|
||||
$this->transaction = \IPS\nexus\Transaction::load( $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() );
|
||||
}
|
||||
elseif ( isset( $this->data['data']['object']['redirect']['return_url'] ) )
|
||||
{
|
||||
$url = new \IPS\Http\Url( $this->data['data']['object']['redirect']['return_url'] );
|
||||
$this->transaction = \IPS\nexus\Transaction::load( $url->queryString['nexusTransactionId'] );
|
||||
/* @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() );
|
||||
}
|
||||
elseif ( isset( $this->data['data']['object']['charge'] ) )
|
||||
{
|
||||
@@ -492,11 +549,13 @@ class stripeWebhookHandler
|
||||
|
||||
if ( $paymentIntentId )
|
||||
{
|
||||
$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'] ) )->first() );
|
||||
/* @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() );
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->transaction = \IPS\nexus\Transaction::constructFromData( \IPS\Db::i()->select( '*', 'nexus_transactions', array( 't_gw_id=?', $this->data['data']['object']['charge'] ) )->first() );
|
||||
{
|
||||
/* @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() );
|
||||
}
|
||||
}
|
||||
elseif ( isset( $this->data['data']['object']['id'] ) and mb_substr( $this->data['data']['object']['id'], 0, 4 ) !== 'src_' )
|
||||
@@ -517,7 +576,8 @@ class stripeWebhookHandler
|
||||
|
||||
try
|
||||
{
|
||||
$this->transaction = \IPS\nexus\Transaction::constructFromData( \IPS\Db::i()->select( '*', 'nexus_transactions', $where )->first() );
|
||||
/* @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() );
|
||||
}
|
||||
catch ( \UnderflowException $e )
|
||||
{
|
||||
@@ -608,6 +668,8 @@ class stripeWebhookHandler
|
||||
return $this->disputeCreated();
|
||||
case 'charge.dispute.closed':
|
||||
return $this->disputeClosed();
|
||||
/*case 'payment_intent.payment_failed':
|
||||
return $this->paymentIntentFailed();*/
|
||||
default:
|
||||
return 'UNNEEDED_TYPE';
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user