mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
Merge pull request #7981 from turbo124/v5-develop
Refactor Stripe Webhook to support all API Versions.
This commit is contained in:
commit
d82905a1dd
@ -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()) {
|
||||
|
||||
|
@ -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);
|
||||
|
@ -40,7 +40,6 @@ class Task extends BaseModel
|
||||
'number',
|
||||
'is_date_based',
|
||||
'status_order',
|
||||
'invoice_lock'
|
||||
];
|
||||
|
||||
protected $touches = [];
|
||||
|
@ -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,
|
||||
];
|
||||
|
||||
|
@ -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,
|
||||
];
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -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
@ -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);
|
||||
|
||||
}
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user