Merge pull request #7981 from turbo124/v5-develop

Refactor Stripe Webhook to support all API Versions.
This commit is contained in:
David Bomba 2022-11-26 10:18:12 +11:00 committed by GitHub
commit d82905a1dd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 121 additions and 2634 deletions

View File

@ -48,7 +48,7 @@ class Kernel extends ConsoleKernel
$schedule->job(new VersionCheck)->daily();
/* Checks and cleans redundant files */
$schedule->job(new DiskCleanup)->daily()->withoutOverlapping();
$schedule->job(new DiskCleanup)->dailyAt('02:10')->withoutOverlapping();
/* Send reminders */
$schedule->job(new ReminderJob)->hourly()->withoutOverlapping();
@ -63,7 +63,7 @@ class Kernel extends ConsoleKernel
$schedule->job(new UpdateExchangeRates)->dailyAt('23:30')->withoutOverlapping();
/* Runs cleanup code for subscriptions */
$schedule->job(new SubscriptionCron)->daily()->withoutOverlapping();
$schedule->job(new SubscriptionCron)->dailyAt('00:01')->withoutOverlapping();
/* Sends recurring invoices*/
$schedule->job(new RecurringInvoicesCron)->hourly()->withoutOverlapping();
@ -72,22 +72,22 @@ class Kernel extends ConsoleKernel
$schedule->job(new RecurringExpensesCron)->dailyAt('00:10')->withoutOverlapping();
/* Fires notifications for expired Quotes */
$schedule->job(new QuoteCheckExpired)->dailyAt('05:00')->withoutOverlapping();
$schedule->job(new QuoteCheckExpired)->dailyAt('05:10')->withoutOverlapping();
/* Performs auto billing */
$schedule->job(new AutoBillCron)->dailyAt('06:00')->withoutOverlapping();
$schedule->job(new AutoBillCron)->dailyAt('06:20')->withoutOverlapping();
/* Checks the status of the scheduler */
$schedule->job(new SchedulerCheck)->daily()->withoutOverlapping();
$schedule->job(new SchedulerCheck)->dailyAt('01:10')->withoutOverlapping();
/* Checks for scheduled tasks */
$schedule->job(new TaskScheduler())->daily()->withoutOverlapping();
$schedule->job(new TaskScheduler())->dailyAt('06:50')->withoutOverlapping();
/* Performs system maintenance such as pruning the backup table */
$schedule->job(new SystemMaintenance)->weekly()->withoutOverlapping();
/* Pulls in bank transactions from third party services */
$schedule->job(new BankTransactionSync)->dailyAt('04:00')->withoutOverlapping();
$schedule->job(new BankTransactionSync)->dailyAt('04:10')->withoutOverlapping();
if (Ninja::isSelfHost()) {

View File

@ -31,7 +31,7 @@ class UpdateTaskRequest extends Request
public function authorize() : bool
{
//prevent locked tasks from updating
if($this->task->invoice_lock && $this->task->invoice_id)
if($this->task->invoice_id && $this->task->company->invoice_task_lock)
return false;
return auth()->user()->can('edit', $this->task);

View File

@ -40,7 +40,6 @@ class Task extends BaseModel
'number',
'is_date_based',
'status_order',
'invoice_lock'
];
protected $touches = [];

View File

@ -138,7 +138,7 @@ class CreditCard
'payment_method' => $this->stripe->payment_hash->data->server_response->payment_method,
'payment_type' => PaymentType::parseCardType(strtolower($stripe_method->card->brand)) ?: PaymentType::CREDIT_CARD_OTHER,
'amount' => $this->stripe->convertFromStripeAmount($this->stripe->payment_hash->data->server_response->amount, $this->stripe->client->currency()->precision, $this->stripe->client->currency()),
'transaction_reference' => optional($this->stripe->payment_hash->data->payment_intent->charges->data[0])->id,
'transaction_reference' => isset($this->stripe->payment_hash->data->payment_intent->latest_charge) ? $this->stripe->payment_hash->data->payment_intent->latest_charge : optional($this->stripe->payment_hash->data->payment_intent->charges->data[0])->id,
'gateway_type_id' => GatewayType::CREDIT_CARD,
];

View File

@ -92,75 +92,91 @@ class PaymentIntentWebhook implements ShouldQueue
if($this->payment_completed)
return;
$company_gateway = CompanyGateway::find($this->company_gateway_id);
$stripe_driver = $company_gateway->driver()->init();
if(isset($this->stripe_request['object']['charges']) && optional($this->stripe_request['object']['charges']['data'][0])['id']){
$charge_id = false;
$company = Company::where('company_key', $this->company_key)->first();
$payment = Payment::query()
->where('company_id', $company->id)
->where('transaction_reference', $this->stripe_request['object']['charges']['data'][0]['id'])
->first();
//return early
if($payment && $payment->status_id == Payment::STATUS_COMPLETED){
nlog(" payment found and status correct - returning ");
return;
}
elseif($payment){
$payment->status_id = Payment::STATUS_COMPLETED;
$payment->save();
}
if(isset($this->stripe_request['object']['charges']) && optional($this->stripe_request['object']['charges']['data'][0])['id'])
$charge_id = $this->stripe_request['object']['charges']['data'][0]['id']; // API VERSION 2018
elseif (isset($this->stripe_request['object']['latest_charge']))
$charge_id = $this->stripe_request['object']['latest_charge']; // API VERSION 2022-11-15
$hash = optional($this->stripe_request['object']['charges']['data'][0]['metadata'])['payment_hash'];
$payment_hash = PaymentHash::where('hash', $hash)->first();
if(!$payment_hash)
return;
nlog("payment intent");
nlog($this->stripe_request);
if(array_key_exists('allowed_source_types', $this->stripe_request['object']) && optional($this->stripe_request['object']['charges']['data'][0]['metadata']['payment_hash']) && in_array('card', $this->stripe_request['object']['allowed_source_types']))
{
nlog("hash found");
$hash = $this->stripe_request['object']['charges']['data'][0]['metadata']['payment_hash'];
$payment_hash = PaymentHash::where('hash', $hash)->first();
$invoice = Invoice::with('client')->find($payment_hash->fee_invoice_id);
$client = $invoice->client;
$this->updateCreditCardPayment($payment_hash, $client);
}
elseif(array_key_exists('payment_method_types', $this->stripe_request['object']) && optional($this->stripe_request['object']['charges']['data'][0]['metadata']['payment_hash']) && in_array('card', $this->stripe_request['object']['payment_method_types']))
{
nlog("hash found");
$hash = $this->stripe_request['object']['charges']['data'][0]['metadata']['payment_hash'];
$payment_hash = PaymentHash::where('hash', $hash)->first();
$invoice = Invoice::with('client')->find($payment_hash->fee_invoice_id);
$client = $invoice->client;
$this->updateCreditCardPayment($payment_hash, $client);
}
elseif(array_key_exists('payment_method_types', $this->stripe_request['object']) && optional($this->stripe_request['object']['charges']['data'][0]['metadata']['payment_hash']) && in_array('us_bank_account', $this->stripe_request['object']['payment_method_types']))
{
nlog("hash found");
$hash = $this->stripe_request['object']['charges']['data'][0]['metadata']['payment_hash'];
$payment_hash = PaymentHash::where('hash', $hash)->first();
$invoice = Invoice::with('client')->find($payment_hash->fee_invoice_id);
$client = $invoice->client;
$this->updateAchPayment($payment_hash, $client);
}
if(!$charge_id){
nlog("could not resolve charge");
return;
}
$pi = \Stripe\PaymentIntent::retrieve($this->stripe_request['object']['id'], $stripe_driver->stripe_connect_auth);
$charge = \Stripe\Charge::retrieve($charge_id, $stripe_driver->stripe_connect_auth);
if(!$charge)
{
nlog("no charge found");
nlog($this->stripe_request);
return;
}
$company = Company::where('company_key', $this->company_key)->first();
$payment = Payment::query()
->where('company_id', $company->id)
->where('transaction_reference', $charge['id'])
->first();
//return early
if($payment && $payment->status_id == Payment::STATUS_COMPLETED){
nlog(" payment found and status correct - returning ");
return;
}
elseif($payment){
$payment->status_id = Payment::STATUS_COMPLETED;
$payment->save();
}
$hash = isset($charge['metadata']['payment_hash']) ? $charge['metadata']['payment_hash'] : false;
$payment_hash = PaymentHash::where('hash', $hash)->first();
if(!$payment_hash)
return;
$stripe_driver->client = $payment_hash->fee_invoice->client;
$meta = [
'gateway_type_id' => $pi['metadata']['gateway_type_id'],
'transaction_reference' => $charge['id'],
'customer' => $charge['customer'],
'payment_method' => $charge['payment_method'],
'card_details' => isset($charge['payment_method_details']['card']['brand']) ? $charge['payment_method_details']['card']['brand'] : PaymentType::CREDIT_CARD_OTHER
];
if(isset($pi['allowed_source_types']) && in_array('card', $pi['allowed_source_types']))
{
$invoice = Invoice::with('client')->find($payment_hash->fee_invoice_id);
$client = $invoice->client;
$this->updateCreditCardPayment($payment_hash, $client, $meta);
}
elseif(isset($pi['payment_method_types']) && in_array('card', $pi['payment_method_types']))
{
$invoice = Invoice::with('client')->find($payment_hash->fee_invoice_id);
$client = $invoice->client;
$this->updateCreditCardPayment($payment_hash, $client, $meta);
}
elseif(isset($pi['payment_method_types']) && in_array('us_bank_account', $pi['payment_method_types']))
{
$invoice = Invoice::with('client')->find($payment_hash->fee_invoice_id);
$client = $invoice->client;
$this->updateAchPayment($payment_hash, $client, $meta);
}
SystemLogger::dispatch(
['response' => $this->stripe_request, 'data' => []],
@ -174,10 +190,10 @@ class PaymentIntentWebhook implements ShouldQueue
}
private function updateAchPayment($payment_hash, $client)
private function updateAchPayment($payment_hash, $client, $meta)
{
$company_gateway = CompanyGateway::find($this->company_gateway_id);
$payment_method_type = optional($this->stripe_request['object']['charges']['data'][0]['metadata'])['gateway_type_id'];
$payment_method_type = $meta['gateway_type_id'];
$driver = $company_gateway->driver($client)->init()->setPaymentMethod($payment_method_type);
$payment_hash->data = array_merge((array) $payment_hash->data, $this->stripe_request);
@ -188,7 +204,7 @@ class PaymentIntentWebhook implements ShouldQueue
'payment_method' => $payment_hash->data->object->payment_method,
'payment_type' => PaymentType::ACH,
'amount' => $payment_hash->data->amount_with_fee,
'transaction_reference' => $this->stripe_request['object']['charges']['data'][0]['id'],
'transaction_reference' => $meta['transaction_reference'],
'gateway_type_id' => GatewayType::BANK_TRANSFER,
];
@ -205,9 +221,9 @@ class PaymentIntentWebhook implements ShouldQueue
try {
$customer = $driver->getCustomer($this->stripe_request['object']['charges']['data'][0]['customer']);
$method = $driver->getStripePaymentMethod($this->stripe_request['object']['charges']['data'][0]['payment_method']);
$payment_method = $this->stripe_request['object']['charges']['data'][0]['payment_method'];
$customer = $driver->getCustomer($meta['customer']);
$method = $driver->getStripePaymentMethod($meta['payment_method']);
$payment_method = $meta['payment_method'];
$token_exists = ClientGatewayToken::where([
'gateway_customer_reference' => $customer->id,
@ -249,10 +265,10 @@ class PaymentIntentWebhook implements ShouldQueue
}
private function updateCreditCardPayment($payment_hash, $client)
private function updateCreditCardPayment($payment_hash, $client, $meta)
{
$company_gateway = CompanyGateway::find($this->company_gateway_id);
$payment_method_type = optional($this->stripe_request['object']['charges']['data'][0]['metadata'])['gateway_type_id'];
$payment_method_type = $meta['gateway_type_id'];
$driver = $company_gateway->driver($client)->init()->setPaymentMethod($payment_method_type);
$payment_hash->data = array_merge((array) $payment_hash->data, $this->stripe_request);
@ -261,9 +277,9 @@ class PaymentIntentWebhook implements ShouldQueue
$data = [
'payment_method' => $payment_hash->data->object->payment_method,
'payment_type' => PaymentType::parseCardType(strtolower(optional($this->stripe_request['object']['charges']['data'][0]['payment_method_details']['card'])['brand'])) ?: PaymentType::CREDIT_CARD_OTHER,
'payment_type' => PaymentType::parseCardType(strtolower($meta['card_details'])) ?: PaymentType::CREDIT_CARD_OTHER,
'amount' => $payment_hash->data->amount_with_fee,
'transaction_reference' => $this->stripe_request['object']['charges']['data'][0]['id'],
'transaction_reference' => $meta['transaction_reference'],
'gateway_type_id' => GatewayType::CREDIT_CARD,
];

View File

@ -116,11 +116,14 @@ class StripePaymentDriver extends BaseDriver
throw new StripeConnectFailure('Stripe Connect has not been configured');
}
} else {
$this->stripe = new StripeClient(
$this->company_gateway->getConfigField('apiKey')
);
Stripe::setApiKey($this->company_gateway->getConfigField('apiKey'));
// Stripe::setApiVersion('2022-11-15');
}
return $this;

View File

@ -92,7 +92,6 @@ class TaskTransformer extends EntityTransformer
'status_sort_order' => (int) $task->status_sort_order, //deprecated 5.0.34
'is_date_based' => (bool) $task->is_date_based,
'status_order' => is_null($task->status_order) ? null : (int) $task->status_order,
'invoice_lock' => (bool) $task->invoice_lock,
];
}
}

View File

@ -14,10 +14,6 @@ return new class extends Migration
*/
public function up()
{
Schema::table('tasks', function (Blueprint $table)
{
$table->boolean('invoice_lock')->default(false);
});
Schema::table('companies', function (Blueprint $table)
{

File diff suppressed because it is too large Load Diff

View File

@ -81,9 +81,9 @@ class TaskApiTest extends TestCase
$response->assertStatus(200);
$task = Task::find($this->decodePrimaryKey($arr['data']['id']));
$task->invoice_lock =true;
$task->company->invoice_task_lock = true;
$task->invoice_id = $this->invoice->id;
$task->save();
$task->push();
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
@ -97,32 +97,31 @@ class TaskApiTest extends TestCase
}
public function testTaskLocking()
{
$data = [
'timelog' => [[1,2],[3,4]],
'invoice_lock' => true
];
// public function testTaskLocking()
// {
// $data = [
// 'timelog' => [[1,2],[3,4]],
// ];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/tasks', $data);
// $response = $this->withHeaders([
// 'X-API-SECRET' => config('ninja.api_secret'),
// 'X-API-TOKEN' => $this->token,
// ])->post('/api/v1/tasks', $data);
$arr = $response->json();
$response->assertStatus(200);
// $arr = $response->json();
// $response->assertStatus(200);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->putJson('/api/v1/tasks/' . $arr['data']['id'], $data);
// $response = $this->withHeaders([
// 'X-API-SECRET' => config('ninja.api_secret'),
// 'X-API-TOKEN' => $this->token,
// ])->putJson('/api/v1/tasks/' . $arr['data']['id'], $data);
$arr = $response->json();
// $arr = $response->json();
$response->assertStatus(200);
// $response->assertStatus(200);
}
// }