Added intuit payments
This commit is contained in:
parent
a518158ccf
commit
8ad9e73abb
@ -48,6 +48,7 @@ AUTH_GOOGLE_SECRET=
|
|||||||
|
|
||||||
AUTH_INTUIT_CLIENT_ID=
|
AUTH_INTUIT_CLIENT_ID=
|
||||||
AUTH_INTUIT_SECRET_KEY=
|
AUTH_INTUIT_SECRET_KEY=
|
||||||
|
INTUIT_VERIFYTOKEN=
|
||||||
|
|
||||||
PAYPAL_MODE=sandbox
|
PAYPAL_MODE=sandbox
|
||||||
PAYPAL_SANDBOX_CLIENT_ID=
|
PAYPAL_SANDBOX_CLIENT_ID=
|
||||||
|
58
app/Console/Commands/AccountingPaymentGet.php
Normal file
58
app/Console/Commands/AccountingPaymentGet.php
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use GuzzleHttp\Exception\ConnectException;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Config;
|
||||||
|
use Intuit\Exceptions\ConnectionIssueException;
|
||||||
|
|
||||||
|
use App\Models\{ProviderOauth,Site,User};
|
||||||
|
|
||||||
|
class AccountingPaymentGet extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'accounting:payment:get'
|
||||||
|
.' {siteid : Site ID}'
|
||||||
|
.' {provider : Provider Name}'
|
||||||
|
.' {user : User Email}'
|
||||||
|
.' {id : Payment ID}';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Get a payment from the accounting provider';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$site = Site::findOrFail($this->argument('siteid'));
|
||||||
|
Config::set('site',$site);
|
||||||
|
|
||||||
|
$uo = User::where('email',$this->argument('user'))->singleOrFail();
|
||||||
|
|
||||||
|
$so = ProviderOauth::where('name',$this->argument('provider'))->singleOrFail();
|
||||||
|
if (! ($to=$so->token($uo)))
|
||||||
|
abort(500,sprintf('Unknown Tokens for [%s]',$uo->email));
|
||||||
|
|
||||||
|
try {
|
||||||
|
$api = $to->API();
|
||||||
|
dump($api->getPaymentQuery($this->argument('id')));
|
||||||
|
|
||||||
|
} catch (ConnectException|ConnectionIssueException $e) {
|
||||||
|
$this->error($e->getMessage());
|
||||||
|
|
||||||
|
return Command::FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
50
app/Console/Commands/AccountingPaymentSync.php
Normal file
50
app/Console/Commands/AccountingPaymentSync.php
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Config;
|
||||||
|
|
||||||
|
use App\Models\{ProviderOauth,Site,User};
|
||||||
|
use App\Jobs\AccountingPaymentSync as Job;
|
||||||
|
|
||||||
|
class AccountingPaymentSync extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'accounting:payment:sync'
|
||||||
|
.' {siteid : Site ID}'
|
||||||
|
.' {provider : Provider Name}'
|
||||||
|
.' {user : User Email}';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Synchronise payments with accounting system';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$site = Site::findOrFail($this->argument('siteid'));
|
||||||
|
Config::set('site',$site);
|
||||||
|
|
||||||
|
$uo = User::where('email',$this->argument('user'))->singleOrFail();
|
||||||
|
|
||||||
|
$so = ProviderOauth::where('name',$this->argument('provider'))->singleOrFail();
|
||||||
|
if (! ($to=$so->token($uo)))
|
||||||
|
abort(500,sprintf('Unknown Tokens for [%s]',$uo->email));
|
||||||
|
|
||||||
|
$api = $to->API();
|
||||||
|
foreach ($api->getPayments() as $acc)
|
||||||
|
Job::dispatchSync($to,$acc);
|
||||||
|
}
|
||||||
|
}
|
@ -2,9 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
|
|
||||||
use App\Models\ProviderOauth;
|
use App\Models\ProviderOauth;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
@ -38,9 +36,4 @@ class AccountingController extends Controller
|
|||||||
->transform(function($item,$value) { return ['id'=>$value,'value'=>$item]; })
|
->transform(function($item,$value) { return ['id'=>$value,'value'=>$item]; })
|
||||||
->values();
|
->values();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function webhook(Request $request)
|
|
||||||
{
|
|
||||||
Log::channel('webhook')->debug('Webhook event',['request'=>$request]);
|
|
||||||
}
|
|
||||||
}
|
}
|
136
app/Jobs/AccountingPaymentSync.php
Normal file
136
app/Jobs/AccountingPaymentSync.php
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
use App\Models\{Account,Invoice,Payment,PaymentItem,ProviderToken};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronise payments with our payments.
|
||||||
|
*
|
||||||
|
* This will:
|
||||||
|
* + Create/Update the payment in our system
|
||||||
|
* + Assocate the payment to the same invoice in our system
|
||||||
|
*/
|
||||||
|
class AccountingPaymentSync implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
private const LOGKEY = 'JPS';
|
||||||
|
|
||||||
|
private \Intuit\Models\Payment $pmi;
|
||||||
|
private ProviderToken $to;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new job instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(ProviderToken $to,\Intuit\Models\Payment $pmi)
|
||||||
|
{
|
||||||
|
$this->pmi = $pmi;
|
||||||
|
$this->to = $to;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the job.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
// See if we are already linked
|
||||||
|
if (($x=$this->to->provider->payments->where('pivot.ref',$this->pmi->id))->count() === 1) {
|
||||||
|
$o = $x->pop();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Find the account
|
||||||
|
$ao = Account::select('accounts.*')
|
||||||
|
->join('account__provider',['account__provider.account_id'=>'accounts.id'])
|
||||||
|
->where('provider_oauth_id',$this->to->provider_oauth_id)
|
||||||
|
->where('ref',$this->pmi->account_ref)
|
||||||
|
->single();
|
||||||
|
|
||||||
|
if (! $ao) {
|
||||||
|
Log::alert(sprintf('%s:Account not found for payment [%s:%d]',self::LOGKEY,$this->pmi->id,$this->pmi->account_ref));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the payment
|
||||||
|
$o = new Payment;
|
||||||
|
$o->account_id = $ao->id;
|
||||||
|
$o->site_id = $ao->site_id; // @todo Automatically determine
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the payment details
|
||||||
|
$o->paid_at = $this->pmi->date_paid;
|
||||||
|
$o->active = TRUE;
|
||||||
|
$o->checkout_id = 2; // @todo
|
||||||
|
$o->total_amt = $this->pmi->total_amt;
|
||||||
|
$o->notes = 'Imported from Intuit';
|
||||||
|
$o->save();
|
||||||
|
|
||||||
|
Log::info(sprintf('%s:Recording payment [%s:%3.2f]',self::LOGKEY,$this->pmi->id,$this->pmi->total_amt));
|
||||||
|
|
||||||
|
$o->providers()->syncWithoutDetaching([
|
||||||
|
$this->to->provider->id => [
|
||||||
|
'ref' => $this->pmi->id,
|
||||||
|
'synctoken' => $this->pmi->synctoken,
|
||||||
|
'created_at'=>Carbon::create($this->pmi->created_at),
|
||||||
|
'updated_at'=>Carbon::create($this->pmi->updated_at),
|
||||||
|
'site_id'=>$this->to->site_id,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Load the invoice that this payment pays
|
||||||
|
$invoices = collect();
|
||||||
|
foreach ($this->pmi->lines() as $item => $amount) {
|
||||||
|
$invoice = Invoice::select('invoices.*')
|
||||||
|
->join('invoice__provider',['invoice__provider.invoice_id'=>'invoices.id'])
|
||||||
|
->where('provider_oauth_id',$this->to->provider_oauth_id)
|
||||||
|
->where('ref',$item)
|
||||||
|
->single();
|
||||||
|
|
||||||
|
$invoices->put($item,$invoice);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete existing paid invoices that are no longer paid
|
||||||
|
foreach ($o->items as $pio)
|
||||||
|
if ($invoices->pluck('id')->search($pio->invoice_id) === FALSE)
|
||||||
|
$pio->delete();
|
||||||
|
|
||||||
|
// Update payment items
|
||||||
|
foreach ($this->pmi->lines() as $item => $amount) {
|
||||||
|
if (! $invoices->has($item)) {
|
||||||
|
Log::alert(sprintf('%s:Invoice [%s] not recorded, payment not assigned',self::LOGKEY,$item));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$io = $invoices->get($item);
|
||||||
|
|
||||||
|
// If the payment item already exists
|
||||||
|
if (($x=$o->items->where('invoice_id',$io->id))->count()) {
|
||||||
|
$pio = $x->pop();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$pio = new PaymentItem;
|
||||||
|
$pio->invoice_id = $io->id;
|
||||||
|
$pio->site_id = $io->site_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pio->amount = $amount;
|
||||||
|
$o->items()->save($pio);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::alert(sprintf('%s:Payment updated [%s:%s]',self::LOGKEY,$o->id,$this->pmi->id));
|
||||||
|
}
|
||||||
|
}
|
@ -7,6 +7,7 @@ use Illuminate\Support\Facades\DB;
|
|||||||
use Leenooks\Traits\ScopeActive;
|
use Leenooks\Traits\ScopeActive;
|
||||||
|
|
||||||
use App\Interfaces\IDs;
|
use App\Interfaces\IDs;
|
||||||
|
use App\Traits\ProviderRef;
|
||||||
use App\Traits\PushNew;
|
use App\Traits\PushNew;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -24,7 +25,7 @@ use App\Traits\PushNew;
|
|||||||
*/
|
*/
|
||||||
class Payment extends Model implements IDs
|
class Payment extends Model implements IDs
|
||||||
{
|
{
|
||||||
use PushNew,ScopeActive;
|
use PushNew,ScopeActive,ProviderRef;
|
||||||
|
|
||||||
protected $dates = [
|
protected $dates = [
|
||||||
'paid_at',
|
'paid_at',
|
||||||
@ -75,6 +76,13 @@ class Payment extends Model implements IDs
|
|||||||
return $this->hasMany(PaymentItem::class);
|
return $this->hasMany(PaymentItem::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function providers()
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(ProviderOauth::class,'payment__provider')
|
||||||
|
->where('payment__provider.site_id',$this->site_id)
|
||||||
|
->withPivot('ref','synctoken','created_at','updated_at');
|
||||||
|
}
|
||||||
|
|
||||||
/* SCOPES */
|
/* SCOPES */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,6 +31,13 @@ class ProviderOauth extends Model
|
|||||||
->withPivot('ref','synctoken','created_at','updated_at');
|
->withPivot('ref','synctoken','created_at','updated_at');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function payments()
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(Payment::class,'payment__provider')
|
||||||
|
->where('payment__provider.site_id',$this->site_id)
|
||||||
|
->withPivot('ref','synctoken','created_at','updated_at');
|
||||||
|
}
|
||||||
|
|
||||||
public function taxes()
|
public function taxes()
|
||||||
{
|
{
|
||||||
return $this->belongsToMany(Tax::class,'tax__provider')
|
return $this->belongsToMany(Tax::class,'tax__provider')
|
||||||
|
24
composer.lock
generated
24
composer.lock
generated
@ -1289,25 +1289,25 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "firebase/php-jwt",
|
"name": "firebase/php-jwt",
|
||||||
"version": "v6.4.0",
|
"version": "v6.5.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/firebase/php-jwt.git",
|
"url": "https://github.com/firebase/php-jwt.git",
|
||||||
"reference": "4dd1e007f22a927ac77da5a3fbb067b42d3bc224"
|
"reference": "e94e7353302b0c11ec3cfff7180cd0b1743975d2"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/firebase/php-jwt/zipball/4dd1e007f22a927ac77da5a3fbb067b42d3bc224",
|
"url": "https://api.github.com/repos/firebase/php-jwt/zipball/e94e7353302b0c11ec3cfff7180cd0b1743975d2",
|
||||||
"reference": "4dd1e007f22a927ac77da5a3fbb067b42d3bc224",
|
"reference": "e94e7353302b0c11ec3cfff7180cd0b1743975d2",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^7.1||^8.0"
|
"php": "^7.4||^8.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"guzzlehttp/guzzle": "^6.5||^7.4",
|
"guzzlehttp/guzzle": "^6.5||^7.4",
|
||||||
"phpspec/prophecy-phpunit": "^1.1",
|
"phpspec/prophecy-phpunit": "^2.0",
|
||||||
"phpunit/phpunit": "^7.5||^9.5",
|
"phpunit/phpunit": "^9.5",
|
||||||
"psr/cache": "^1.0||^2.0",
|
"psr/cache": "^1.0||^2.0",
|
||||||
"psr/http-client": "^1.0",
|
"psr/http-client": "^1.0",
|
||||||
"psr/http-factory": "^1.0"
|
"psr/http-factory": "^1.0"
|
||||||
@ -1346,9 +1346,9 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/firebase/php-jwt/issues",
|
"issues": "https://github.com/firebase/php-jwt/issues",
|
||||||
"source": "https://github.com/firebase/php-jwt/tree/v6.4.0"
|
"source": "https://github.com/firebase/php-jwt/tree/v6.5.0"
|
||||||
},
|
},
|
||||||
"time": "2023-02-09T21:01:23+00:00"
|
"time": "2023-05-12T15:47:07+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "fruitcake/laravel-cors",
|
"name": "fruitcake/laravel-cors",
|
||||||
@ -3681,11 +3681,11 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "leenooks/intuit",
|
"name": "leenooks/intuit",
|
||||||
"version": "0.1.3",
|
"version": "0.1.4",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://dev.dege.au/leenooks/intuit",
|
"url": "https://dev.dege.au/leenooks/intuit",
|
||||||
"reference": "2f1b34a806c19877379c588cf1b53f8ac3474d42"
|
"reference": "2f75c7dd1fa6179e25f06ba2a617993858ccb7eb"
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"jenssegers/model": "^1.5"
|
"jenssegers/model": "^1.5"
|
||||||
@ -3715,7 +3715,7 @@
|
|||||||
"laravel",
|
"laravel",
|
||||||
"leenooks"
|
"leenooks"
|
||||||
],
|
],
|
||||||
"time": "2023-05-12T12:41:53+00:00"
|
"time": "2023-05-13T11:17:36+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "leenooks/laravel",
|
"name": "leenooks/laravel",
|
||||||
|
@ -46,6 +46,7 @@ return [
|
|||||||
'provider' => [
|
'provider' => [
|
||||||
'intuit' => [
|
'intuit' => [
|
||||||
'api'=> \Intuit\API::class,
|
'api'=> \Intuit\API::class,
|
||||||
|
'verifytoken' => env('INTUIT_VERIFYTOKEN'),
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
|
||||||
|
38
database/migrations/2023_05_13_142113_accounting_payment.php
Normal file
38
database/migrations/2023_05_13_142113_accounting_payment.php
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::create('payment__provider', function (Blueprint $table) {
|
||||||
|
$table->timestamps();
|
||||||
|
$table->integer('payment_id')->unsigned();
|
||||||
|
$table->integer('provider_oauth_id')->unsigned();
|
||||||
|
$table->integer('site_id')->unsigned();
|
||||||
|
$table->string('ref');
|
||||||
|
$table->integer('synctoken');
|
||||||
|
|
||||||
|
$table->foreign(['payment_id','site_id'])->references(['id','site_id'])->on('payments');
|
||||||
|
$table->foreign(['provider_oauth_id','site_id'])->references(['id','site_id'])->on('provider_oauth');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('payment__provider');
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('payment_items', function (Blueprint $table) {
|
||||||
|
$table->unique(['site_id','payment_id','invoice_id']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::table('payment_items', function (Blueprint $table) {
|
||||||
|
$table->dropUnique(['site_id','payment_id','invoice_id']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use Intuit\Controllers\Webhook;
|
||||||
|
|
||||||
use App\Http\Controllers\{AccountingController,
|
use App\Http\Controllers\{AccountingController,
|
||||||
AdminController,
|
AdminController,
|
||||||
CheckoutController,
|
CheckoutController,
|
||||||
@ -38,5 +40,4 @@ Route::group(['middleware'=>'auth:api'], function() {
|
|||||||
Route::any('/intuit/accounting/list',[AccountingController::class,'list']);
|
Route::any('/intuit/accounting/list',[AccountingController::class,'list']);
|
||||||
});
|
});
|
||||||
|
|
||||||
// @todo Take the specific 'intuit' out of this url, to enable other accounting methods
|
Route::any('/intuit/webhook',[Webhook::class,'webhook']);
|
||||||
Route::any('/intuit/webhook',[AccountingController::class,'webhook']);
|
|
Loading…
Reference in New Issue
Block a user