1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-09-20 08:21:34 +02:00

Stripe Connect

This commit is contained in:
David Bomba 2021-04-22 20:38:22 +10:00
commit f62518f63b
44 changed files with 1110 additions and 1175 deletions

View File

@ -1,55 +0,0 @@
<?php
namespace App\Events\ClientSubscription;
use App\Models\ClientSubscription;
use App\Models\Company;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class ClientSubscriptionWasCreated
{
use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* @var ClientSubscription
*/
public $client_subscription;
/**
* @var Company
*/
public $company;
/**
* @var array
*/
public $event_vars;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct(ClientSubscription $client_subscription, Company $company, array $event_vars)
{
$this->client_subscription = $client_subscription;
$this->company = $company;
$this->event_vars = $event_vars;
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('channel-name');
}
}

View File

@ -13,6 +13,8 @@ namespace App\Http\Controllers;
use App\Http\Requests\OneTimeToken\OneTimeRouterRequest;
use App\Http\Requests\OneTimeToken\OneTimeTokenRequest;
use App\Models\Company;
use App\Models\CompanyUser;
use App\Models\User;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Auth;
@ -23,7 +25,6 @@ class OneTimeTokenController extends BaseController
{
private $contexts = [
'stripe_connect_test' => 'https://connect.stripe.com/oauth/authorize?response_type=code&client_id=ca_J2FhIhcf9GT5BlWUNeQ1FhnZACaYZrOI&scope=read_write',
'stripe_connect' => 'https://connect.stripe.com/oauth/authorize?response_type=code&client_id=ca_J2Fh2tZfMlaaItUfbUwBBx4JPss8jCz9&scope=read_write'
];
@ -67,7 +68,6 @@ class OneTimeTokenController extends BaseController
*/
public function create(OneTimeTokenRequest $request)
{
$hash = Str::random(64);
$data = [
@ -76,10 +76,10 @@ class OneTimeTokenController extends BaseController
'context' => $request->input('context'),
];
Cache::put( $hash, $data, 3600 );
Cache::put( $hash, $data, 3600);
return response()->json(['hash' => $hash], 200);
}
public function router(OneTimeRouterRequest $request)
@ -89,9 +89,7 @@ class OneTimeTokenController extends BaseController
MultiDB::findAndSetDbByCompanyKey($data['company_key']);
// $user = User::findOrFail($data['user_id']);
// Auth::login($user, true);
// Cache::forget($request->input('hash'));
$this->sendTo($data['context']);

View File

@ -1,19 +0,0 @@
<?php
/**
* @OA\Schema(
* schema="ClientSubscription",
* type="object",
* @OA\Property(property="id", type="string", example="Opnel5aKBz", description="______"),
* @OA\Property(property="subscription_id", type="string", example="Opnel5aKBz", description="______"),
* @OA\Property(property="recurring_invoice_id", type="string", example="Opnel5aKBz", description="______"),
* @OA\Property(property="company_id", type="string", example="Opnel5aKBz", description="______"),
* @OA\Property(property="client_id", type="string", example="Opnel5aKBz", description="______"),
* @OA\Property(property="trial_started", type="string", example="10-10-2021", description="______"),
* @OA\Property(property="trial_ends", type="string", example="12-10-2021", description="______"),
* @OA\Property(property="is_deleted", type="boolean", example="true", description="______"),
* @OA\Property(property="archived_at", type="number", format="integer", example="1434342123", description="Timestamp"),
* @OA\Property(property="created_at", type="number", format="integer", example="134341234234", description="Timestamp"),
* @OA\Property(property="updated_at", type="number", format="integer", example="134341234234", description="Timestamp"),
* )
*/

View File

@ -0,0 +1,69 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Http\Controllers;
use App\Factory\CompanyGatewayFactory;
use App\Http\Requests\StripeConnect\InitializeStripeConnectRequest;
use App\Models\CompanyGateway;
use App\PaymentDrivers\Stripe\Connect\Account;
use Stripe\Exception\ApiErrorException;
class StripeConnectController extends BaseController
{
/**
* Initialize Stripe Connect flow.
*
* @param string $token One-time token
* @throws ApiErrorException
*/
public function initialize(InitializeStripeConnectRequest $request, string $token)
{
// Should we check if company has set country in the ap? Otherwise this will fail.
$data = [
'type' => 'standard',
'email' => $request->getContact()->email,
'country' => $request->getCompany()->country()->iso_3166_2,
];
$exists = CompanyGateway::query()
->where('gateway_key', 'd14dd26a47cecc30fdd65700bfb67b34')
->where('company_id', $request->getCompany()->id)
->first();
if ($exists) {
return redirect()->route('stripe_connect.return');
}
$account = Account::create($data);
$link = Account::link($account->id, $token);
$company_gateway = CompanyGatewayFactory::create($request->getCompany()->id, $request->getContact()->client->user->id);
$company_gateway->fill([
'gateway_key' => 'd14dd26a47cecc30fdd65700bfb67b34',
'fees_and_limits' => [],
'config' => encrypt(json_encode(['account_id' => $account->id]))
]);
$company_gateway->save();
return redirect($link['url']);
}
public function completed()
{
return render('gateways.stripe.connect.completed');
}
}

View File

@ -1,40 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Http\Requests\ClientSubscription;
use App\Http\Requests\Request;
use App\Models\ClientSubscription;
class CreateClientSubscriptionRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize(): bool
{
return auth()->user()->can('create', ClientSubscription::class);
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
//
];
}
}

View File

@ -1,40 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Http\Requests\ClientSubscription;
use App\Http\Requests\Request;
use Illuminate\Foundation\Http\FormRequest;
class DestroyClientSubscriptionRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return auth()->user()->can('edit', $this->client_subscription);
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
//
];
}
}

View File

@ -1,40 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Http\Requests\ClientSubscription;
use App\Http\Requests\Request;
use Illuminate\Foundation\Http\FormRequest;
class EditClientSubscriptionRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return auth()->user()->can('edit', $this->client_subscription);
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
//
];
}
}

View File

@ -1,40 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Http\Requests\ClientSubscription;
use App\Http\Requests\Request;
use Illuminate\Foundation\Http\FormRequest;
class ShowClientSubscriptionRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize() : bool
{
return auth()->user()->can('view', $this->client_subscription);
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
//
];
}
}

View File

@ -1,38 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Http\Requests\ClientSubscription;
use App\Http\Requests\Request;
use App\Models\ClientSubscription;
class StoreClientSubscriptionRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return auth()->user()->can('create', ClientSubscription::class);
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [];
}
}

View File

@ -1,42 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Http\Requests\ClientSubscription;
use App\Http\Requests\Request;
use App\Utils\Traits\ChecksEntityStatus;
class UpdateClientSubscriptionRequest extends Request
{
use ChecksEntityStatus;
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return auth()->user()->can('edit', $this->client_subscription);
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
//
];
}
}

View File

@ -0,0 +1,71 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Http\Requests\StripeConnect;
use App\Models\ClientContact;
use App\Models\Company;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Cache;
class InitializeStripeConnectRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
return [
//
];
}
/**
* Resolve one-time token instance.
*
* @return mixed
*/
public function getTokenContent()
{
$data = Cache::get($this->token);
abort_if(!$data, 404);
abort_if(!array_key_exists('user_id', $data), 404);
abort_if(!array_key_exists('company_key', $data), 404);
return $data;
}
public function getContact()
{
return ClientContact::findOrFail($this->getTokenContent()['user_id']);
}
public function getCompany()
{
return Company::where('company_key', $this->getTokenContent()['company_key'])->firstOrFail();
}
}

View File

@ -1,55 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class ClientSubscription extends BaseModel
{
use HasFactory;
protected $fillable = [
// 'subscription_id',
// 'recurring_invoice_id',
// 'client_id',
// 'trial_started',
// 'trial_ends',
// 'is_deleted',
];
protected $casts = [
'is_deleted' => 'boolean',
'updated_at' => 'timestamp',
'created_at' => 'timestamp',
'deleted_at' => 'timestamp',
];
public function company(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{
return $this->belongsTo(Company::class);
}
public function recurring_invoice(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{
return $this->belongsTo(RecurringInvoice::class);
}
public function client(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{
return $this->belongsTo(Client::class);
}
public function subscription(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{
return $this->belongsTo(BillingSubscription::class);
}
}

View File

@ -1,72 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Observers;
use App\Models\ClientSubscription;
class ClientSubscriptionObserver
{
/**
* Handle the client_subscription "created" event.
*
* @param ClientSubscription $client_subscription
* @return void
*/
public function created(ClientSubscription $client_subscription)
{
//
}
/**
* Handle the client_subscription "updated" event.
*
* @param ClientSubscription $client_subscription
* @return void
*/
public function updated(ClientSubscription $client_subscription)
{
//
}
/**
* Handle the client_subscription "deleted" event.
*
* @param ClientSubscription $client_subscription
* @return void
*/
public function deleted(ClientSubscription $client_subscription)
{
//
}
/**
* Handle the client_subscription "restored" event.
*
* @param ClientSubscription $client_subscription
* @return void
*/
public function restored(ClientSubscription $client_subscription)
{
//
}
/**
* Handle the client_subscription "force deleted" event.
*
* @param ClientSubscription $client_subscription
* @return void
*/
public function forceDeleted(ClientSubscription $client_subscription)
{
//
}
}

View File

@ -91,7 +91,7 @@ class ACH
{
$this->stripe->init();
$bank_account = Customer::retrieveSource($request->customer, $request->source);
$bank_account = Customer::retrieveSource($request->customer, $request->source, $this->stripe->stripe_connect_auth);
try {
$bank_account->verify(['amounts' => request()->transactions]);
@ -121,6 +121,7 @@ class ACH
public function paymentResponse($request)
{
$this->stripe->init();
$source = ClientGatewayToken::query()
@ -152,7 +153,7 @@ class ACH
'currency' => $state['currency'],
'customer' => $state['customer'],
'source' => $state['source'],
]);
], $this->stripe->stripe_connect_auth);
$state = array_merge($state, $request->all());

View File

@ -62,9 +62,9 @@ class Charge
$this->stripe->init();
$local_stripe = new StripeClient(
$this->stripe->company_gateway->getConfigField('apiKey')
);
// $local_stripe = new StripeClient(
// $this->stripe->company_gateway->getConfigField('apiKey')
// );
$response = null;

View File

@ -0,0 +1,210 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\PaymentDrivers\Stripe\Connect;
class Account
{
/**
* @throws \Stripe\Exception\ApiErrorException
*/
public static function create(array $payload): \Stripe\Account
{
$stripe = new \Stripe\StripeClient(
config('ninja.ninja_stripe_key')
);
return $stripe->accounts->create([
'type' => 'standard',
'country' => $payload['country'],
'email' => $payload['email'],
]);
}
/**
* @throws \Stripe\Exception\ApiErrorException
*/
public static function link(string $account_id, string $token): \Stripe\AccountLink
{
$stripe = new \Stripe\StripeClient(
config('ninja.ninja_stripe_key')
);
return $stripe->accountLinks->create([
'account' => $account_id,
'refresh_url' => route('stripe_connect.initialization', $token),
'return_url' => route('stripe_connect.return'),
'type' => 'account_onboarding',
]);
}
/*** If this is a new account (ie there is no account_id in company_gateways.config, the we need to create an account as below.
///
// $stripe = new \Stripe\StripeClient(
// 'sk_test_4eC39HqLyjWDarjtT1zdp7dc'
// );
// $stripe->accounts->create([
// 'type' => 'standard',
// 'country' => 'US', //if we have it - inject
// 'email' => 'jenny.rosen@example.com', //if we have it - inject
// ]);
///
//response
//******************* We should store the 'id' as a property in the config with the key `account_id`
/**
* {
"id": "acct_1032D82eZvKYlo2C",
"object": "account",
"business_profile": {
"mcc": null,
"name": "Stripe.com",
"product_description": null,
"support_address": null,
"support_email": null,
"support_phone": null,
"support_url": null,
"url": null
},
"capabilities": {
"card_payments": "active",
"transfers": "active"
},
"charges_enabled": false,
"country": "US",
"default_currency": "usd",
"details_submitted": false,
"email": "site@stripe.com",
"metadata": {},
"payouts_enabled": false,
"requirements": {
"current_deadline": null,
"currently_due": [
"business_profile.product_description",
"business_profile.support_phone",
"business_profile.url",
"external_account",
"tos_acceptance.date",
"tos_acceptance.ip"
],
"disabled_reason": "requirements.past_due",
"errors": [],
"eventually_due": [
"business_profile.product_description",
"business_profile.support_phone",
"business_profile.url",
"external_account",
"tos_acceptance.date",
"tos_acceptance.ip"
],
"past_due": [],
"pending_verification": []
},
"settings": {
"bacs_debit_payments": {},
"branding": {
"icon": null,
"logo": null,
"primary_color": null,
"secondary_color": null
},
"card_issuing": {
"tos_acceptance": {
"date": null,
"ip": null
}
},
"card_payments": {
"decline_on": {
"avs_failure": true,
"cvc_failure": false
},
"statement_descriptor_prefix": null
},
"dashboard": {
"display_name": "Stripe.com",
"timezone": "US/Pacific"
},
"payments": {
"statement_descriptor": null,
"statement_descriptor_kana": null,
"statement_descriptor_kanji": null
},
"payouts": {
"debit_negative_balances": true,
"schedule": {
"delay_days": 7,
"interval": "daily"
},
"statement_descriptor": null
},
"sepa_debit_payments": {}
},
"type": "standard"
}
*/
//At this stage we have an account, so we need to generate the account link
//then create the account link
// now we start the stripe onboarding flow
// https://stripe.com/docs/api/account_links/object
//
/**
* $stripe = new \Stripe\StripeClient(
'sk_test_4eC39HqLyjWDarjtT1zdp7dc'
);
$stripe->accountLinks->create([
'account' => 'acct_1032D82eZvKYlo2C',
'refresh_url' => 'https://example.com/reauth',
'return_url' => 'https://example.com/return',
'type' => 'account_onboarding',
]);
*/
/**
* Response =
* {
"object": "account_link",
"created": 1618869558,
"expires_at": 1618869858,
"url": "https://connect.stripe.com/setup/s/9BhFaPdfseRF"
}
*/
//The users account may not be active yet, we need to pull the account back and check for the property `charges_enabled`
//
//
// What next?
//
// Now we need to create a superclass of the StripePaymentDriver, i believe the only thing we need to change is the way we initialize the gateway..
/**
*
\Stripe\Stripe::setApiKey("{{PLATFORM_SECRET_KEY}}"); <--- platform secret key = Invoice Ninja secret key
\Stripe\Customer::create(
["email" => "person@example.edu"],
["stripe_account" => "{{CONNECTED_STRIPE_ACCOUNT_ID}}"] <------ company_gateway.config.account_id
);
*/
}

View File

@ -0,0 +1,52 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\PaymentDrivers;
use App\Factory\PaymentFactory;
use App\Http\Requests\Payments\PaymentWebhookRequest;
use App\Http\Requests\Request;
use App\Jobs\Util\SystemLogger;
use App\Models\ClientGatewayToken;
use App\Models\GatewayType;
use App\Models\Payment;
use App\Models\PaymentHash;
use App\Models\SystemLog;
use App\PaymentDrivers\Stripe\ACH;
use App\PaymentDrivers\Stripe\Alipay;
use App\PaymentDrivers\Stripe\Charge;
use App\PaymentDrivers\Stripe\CreditCard;
use App\PaymentDrivers\Stripe\SOFORT;
use App\PaymentDrivers\Stripe\Utilities;
use App\Utils\Traits\MakesHash;
use Exception;
use Illuminate\Support\Carbon;
use Stripe\Customer;
use Stripe\Exception\ApiErrorException;
use Stripe\PaymentIntent;
use Stripe\PaymentMethod;
use Stripe\SetupIntent;
use Stripe\Stripe;
use Stripe\StripeClient;
class StripeConnectPaymentDriver extends StripePaymentDriver
{
public function __construct(CompanyGateway $company_gateway, Client $client = null, $invitation = false)
{
parent::__construct($company_gateway, $client, $invitation);
$this->stripe_connect = true;
}
}

View File

@ -55,6 +55,10 @@ class StripePaymentDriver extends BaseDriver
public $payment_method;
public $stripe_connect = false;
public $stripe_connect_auth = [];
public static $methods = [
GatewayType::CREDIT_CARD => CreditCard::class,
GatewayType::BANK_TRANSFER => ACH::class,
@ -72,11 +76,21 @@ class StripePaymentDriver extends BaseDriver
*/
public function init(): void
{
$this->stripe = new StripeClient(
$this->company_gateway->getConfigField('apiKey')
);
if($this->stripe_connect)
{
Stripe::setApiKey(config('ninja.ninja_stripe_key'));
$this->stripe_connect_auth = ["stripe_account" => $this->company_gateway->getConfigField('account_id')];
}
else
{
$this->stripe = new StripeClient(
$this->company_gateway->getConfigField('apiKey')
);
Stripe::setApiKey($this->company_gateway->getConfigField('apiKey'));
Stripe::setApiKey($this->company_gateway->getConfigField('apiKey'));
}
}
public function setPaymentMethod($payment_method_id)

View File

@ -1,31 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Policies;
use App\Models\User;
/**
* Class ClientSubscriptionPolicy.
*/
class ClientSubscriptionPolicy extends EntityPolicy
{
/**
* Checks if the user has create permissions.
*
* @param User $user
* @return bool
*/
public function create(User $user) : bool
{
return $user->isAdmin() || $user->hasPermission('create_client_subscription') || $user->hasPermission('create_all');
}
}

View File

@ -14,7 +14,6 @@ namespace App\Providers;
use App\Models\Account;
use App\Models\Subscription;
use App\Models\Client;
use App\Models\ClientSubscription;
use App\Models\Company;
use App\Models\CompanyGateway;
use App\Models\CompanyToken;
@ -30,7 +29,6 @@ use App\Models\User;
use App\Observers\AccountObserver;
use App\Observers\SubscriptionObserver;
use App\Observers\ClientObserver;
use App\Observers\ClientSubscriptionObserver;
use App\Observers\CompanyGatewayObserver;
use App\Observers\CompanyObserver;
use App\Observers\CompanyTokenObserver;
@ -82,7 +80,6 @@ class AppServiceProvider extends ServiceProvider
Account::observe(AccountObserver::class);
Subscription::observe(SubscriptionObserver::class);
Client::observe(ClientObserver::class);
ClientSubscription::observe(ClientSubscriptionObserver::class);
Company::observe(CompanyObserver::class);
CompanyGateway::observe(CompanyGatewayObserver::class);
CompanyToken::observe(CompanyTokenObserver::class);

View File

@ -40,7 +40,6 @@ use App\Models\Webhook;
use App\Policies\ActivityPolicy;
use App\Policies\SubscriptionPolicy;
use App\Policies\ClientPolicy;
use App\Policies\ClientSubscriptionPolicy;
use App\Policies\CompanyGatewayPolicy;
use App\Policies\CompanyPolicy;
use App\Policies\CompanyTokenPolicy;

View File

@ -1,28 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Repositories;
use App\Models\ClientSubscription;
class ClientSubscriptionRepository extends BaseRepository
{
public function save($data, ClientSubscription $client_subscription): ?ClientSubscription
{
$client_subscription
->fill($data)
->save();
return $client_subscription;
}
}

View File

@ -20,7 +20,6 @@ use App\Jobs\Util\SubscriptionWebhookHandler;
use App\Jobs\Util\SystemLogger;
use App\Models\Client;
use App\Models\ClientContact;
use App\Models\ClientSubscription;
use App\Models\Invoice;
use App\Models\PaymentHash;
use App\Models\Product;

View File

@ -1,78 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Transformers;
use App\Models\BillingSubscription;
use App\Models\Client;
use App\Models\ClientSubscription;
use App\Models\RecurringInvoice;
use App\Utils\Traits\MakesHash;
class ClientSubscriptionTransformer extends EntityTransformer
{
use MakesHash;
/**
* @var array
*/
protected $defaultIncludes = [];
/**
* @var array
*/
protected $availableIncludes = [
'client',
'recurring_invoice',
'subscription',
];
public function transform(ClientSubscription $client_subscription): array
{
return [
'id' => $this->encodePrimaryKey($client_subscription->id),
'subscription_id' => $this->encodePrimaryKey($client_subscription->subscription_id),
'company_id' => $this->encodePrimaryKey($client_subscription->company_id),
'recurring_invoice_id' => $this->encodePrimaryKey($client_subscription->recurring_invoice_id),
'client_id' => $this->encodePrimaryKey($client_subscription->client_id),
'trial_started' => (string)$client_subscription->trial_started ?: '',
'trial_ends' => (string)$client_subscription->trial_ends ?: '',
'is_deleted' => (bool)$client_subscription->is_deleted,
'created_at' => (int)$client_subscription->created_at,
'updated_at' => (int)$client_subscription->updated_at,
'archived_at' => (int)$client_subscription->deleted_at,
];
}
public function includeClient(ClientSubscription $client_subscription): \League\Fractal\Resource\Item
{
$transformer = new ClientTransformer($this->serializer);
return $this->includeItem($client_subscription->client, $transformer, Client::class);
}
public function includeRecurringInvoice(ClientSubscription $client_subscription): \League\Fractal\Resource\Item
{
$transformer = new RecurringInvoiceTransformer($this->serializer);
return $this->includeItem($client_subscription->recurring_invoice, $transformer, RecurringInvoice::class);
}
public function includeSubscription(ClientSubscription $client_subscription): \League\Fractal\Resource\Item
{
$transformer = new BillingSubscriptionTransformer($this->serializer);
return $this->includeItem($client_subscription->subscription, $transformer, BillingSubscription::class);
}
}

View File

@ -15,7 +15,6 @@ use App\Models\Account;
use App\Models\Activity;
use App\Models\Subscription;
use App\Models\Client;
use App\Models\ClientSubscription;
use App\Models\Company;
use App\Models\CompanyGateway;
use App\Models\CompanyLedger;

View File

@ -147,4 +147,5 @@ return [
'webcron_secret' => env('WEBCRON_SECRET', false),
'disable_auto_update' => env('DISABLE_AUTO_UPDATE', false),
'invoiceninja_hosted_pdf_generation' => env('NINJA_HOSTED_PDF', false),
'ninja_stripe_key' => env('NINJA_STRIPE_KEY', null),
];

View File

@ -0,0 +1,47 @@
<?php
use App\Models\Gateway;
use App\Utils\Ninja;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class StripeConnectGateway extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Model::unguard();
$gateway = [
'id' => 56,
'name' => 'Stripe Connect',
'provider' => 'StripeConnect',
'sort_order' => 1,
'key' => 'd14dd26a47cecc30fdd65700bfb67b34',
'fields' => '{"account_id":""}'
];
Gateway::create($gateway);
if (Ninja::isNinja()) {
Gateway::where('id', 20)->update(['visible' => 0]);
Gateway::where('id', 56)->update(['visible' => 1]);
}
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
}
}

1124
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,2 +1,2 @@
/*! For license information please see stripe-ach.js.LICENSE.txt */
!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/",n(n.s=3)}({3:function(e,t,n){e.exports=n("M5il")},M5il:function(e,t){function n(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}(new(function(){function e(){var t=this;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),r(this,"setupStripe",(function(){return t.stripe=Stripe(t.key),t})),r(this,"getFormData",(function(){return{country:document.getElementById("country").value,currency:document.getElementById("currency").value,routing_number:document.getElementById("routing-number").value,account_number:document.getElementById("account-number").value,account_holder_name:document.getElementById("account-holder-name").value,account_holder_type:document.querySelector('input[name="account-holder-type"]:checked').value}})),r(this,"handleError",(function(e){document.getElementById("save-button").disabled=!1,document.querySelector("#save-button > svg").classList.add("hidden"),document.querySelector("#save-button > span").classList.remove("hidden"),t.errors.textContent="",t.errors.textContent=e,t.errors.hidden=!1})),r(this,"handleSuccess",(function(e){document.getElementById("gateway_response").value=JSON.stringify(e),document.getElementById("server_response").submit()})),r(this,"handleSubmit",(function(e){document.getElementById("save-button").disabled=!0,document.querySelector("#save-button > svg").classList.remove("hidden"),document.querySelector("#save-button > span").classList.add("hidden"),e.preventDefault(),t.errors.textContent="",t.errors.hidden=!0,t.stripe.createToken("bank_account",t.getFormData()).then((function(e){return e.hasOwnProperty("error")?t.handleError(e.error.message):t.handleSuccess(e)}))})),this.errors=document.getElementById("errors"),this.key=document.querySelector('meta[name="stripe-publishable-key"]').content}var t,o,u;return t=e,(o=[{key:"handle",value:function(){var e=this;document.getElementById("save-button").addEventListener("click",(function(t){return e.handleSubmit(t)}))}}])&&n(t.prototype,o),u&&n(t,u),e}())).setupStripe().handle()}});
!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/",n(n.s=3)}({3:function(e,t,n){e.exports=n("M5il")},M5il:function(e,t){function n(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}(new(function(){function e(){var t=this;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),r(this,"setupStripe",(function(){return t.stripe=Stripe(t.key),t.stripe_connect&&(t.stripe.stripeAccount=t.stripe_connect),t})),r(this,"getFormData",(function(){return{country:document.getElementById("country").value,currency:document.getElementById("currency").value,routing_number:document.getElementById("routing-number").value,account_number:document.getElementById("account-number").value,account_holder_name:document.getElementById("account-holder-name").value,account_holder_type:document.querySelector('input[name="account-holder-type"]:checked').value}})),r(this,"handleError",(function(e){document.getElementById("save-button").disabled=!1,document.querySelector("#save-button > svg").classList.add("hidden"),document.querySelector("#save-button > span").classList.remove("hidden"),t.errors.textContent="",t.errors.textContent=e,t.errors.hidden=!1})),r(this,"handleSuccess",(function(e){document.getElementById("gateway_response").value=JSON.stringify(e),document.getElementById("server_response").submit()})),r(this,"handleSubmit",(function(e){document.getElementById("save-button").disabled=!0,document.querySelector("#save-button > svg").classList.remove("hidden"),document.querySelector("#save-button > span").classList.add("hidden"),e.preventDefault(),t.errors.textContent="",t.errors.hidden=!0,t.stripe.createToken("bank_account",t.getFormData()).then((function(e){return e.hasOwnProperty("error")?t.handleError(e.error.message):t.handleSuccess(e)}))})),this.errors=document.getElementById("errors"),this.key=document.querySelector('meta[name="stripe-publishable-key"]').content,this.stripe_connect=document.querySelector('meta[name="stripe-account-id"]').content}var t,o,u;return t=e,(o=[{key:"handle",value:function(){var e=this;document.getElementById("save-button").addEventListener("click",(function(t){return e.handleSubmit(t)}))}}])&&n(t.prototype,o),u&&n(t,u),e}())).setupStripe().handle()}});

View File

@ -1,2 +1,2 @@
/*! For license information please see stripe-alipay.js.LICENSE.txt */
!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/",n(n.s=7)}({"+keB":function(e,t){function n(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}new function e(t){var r=this;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),n(this,"setupStripe",(function(){return r.stripe=Stripe(r.key),r})),n(this,"handle",(function(){var e={type:"alipay",amount:document.querySelector('meta[name="amount"]').content,currency:document.querySelector('meta[name="currency"]').content,redirect:{return_url:document.querySelector('meta[name="return-url"]').content}};document.getElementById("pay-now").addEventListener("click",(function(t){document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.add("hidden"),document.querySelector("#pay-now > span").classList.remove("hidden"),r.stripe.createSource(e).then((function(e){if(e.hasOwnProperty("source"))return window.location=e.source.redirect.url;document.getElementById("pay-now").disabled=!1,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),this.errors.textContent="",this.errors.textContent=e.error.message,this.errors.hidden=!1}))}))})),this.key=t,this.errors=document.getElementById("errors")}(document.querySelector('meta[name="stripe-publishable-key"]').content).setupStripe().handle()},7:function(e,t,n){e.exports=n("+keB")}});
!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/",n(n.s=7)}({"+keB":function(e,t){function n(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}new function e(t,r){var o=this;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),n(this,"setupStripe",(function(){return o.stripe=Stripe(o.key),o.stripeConnect&&(o.stripe.stripeAccount=o.stripeConnect),o})),n(this,"handle",(function(){var e={type:"alipay",amount:document.querySelector('meta[name="amount"]').content,currency:document.querySelector('meta[name="currency"]').content,redirect:{return_url:document.querySelector('meta[name="return-url"]').content}};document.getElementById("pay-now").addEventListener("click",(function(t){document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.add("hidden"),document.querySelector("#pay-now > span").classList.remove("hidden"),o.stripe.createSource(e).then((function(e){if(e.hasOwnProperty("source"))return window.location=e.source.redirect.url;document.getElementById("pay-now").disabled=!1,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),this.errors.textContent="",this.errors.textContent=e.error.message,this.errors.hidden=!1}))}))})),this.key=t,this.stripeConnect=r,this.errors=document.getElementById("errors")}(document.querySelector('meta[name="stripe-publishable-key"]').content,document.querySelector('meta[name="stripe-account-id"]').content).setupStripe().handle()},7:function(e,t,n){e.exports=n("+keB")}});

File diff suppressed because one or more lines are too long

View File

@ -1,2 +1,2 @@
/*! For license information please see stripe-sofort.js.LICENSE.txt */
!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/",n(n.s=6)}({6:function(e,t,n){e.exports=n("RFiP")},RFiP:function(e,t){function n(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}new function e(t){var r=this;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),n(this,"setupStripe",(function(){return r.stripe=Stripe(r.key),r})),n(this,"handle",(function(){var e={type:"sofort",amount:document.querySelector('meta[name="amount"]').content,currency:"eur",redirect:{return_url:document.querySelector('meta[name="return-url"]').content},sofort:{country:document.querySelector('meta[name="country"]').content}};document.getElementById("pay-now").addEventListener("click",(function(t){document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),r.stripe.createSource(e).then((function(e){if(e.hasOwnProperty("source"))return window.location=e.source.redirect.url;document.getElementById("pay-now").disabled=!1,document.querySelector("#pay-now > svg").classList.add("hidden"),document.querySelector("#pay-now > span").classList.remove("hidden"),this.errors.textContent="",this.errors.textContent=e.error.message,this.errors.hidden=!1,document.getElementById("pay-now").disabled=!1}))}))})),this.key=t,this.errors=document.getElementById("errors")}(document.querySelector('meta[name="stripe-publishable-key"]').content).setupStripe().handle()}});
!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/",n(n.s=6)}({6:function(e,t,n){e.exports=n("RFiP")},RFiP:function(e,t){function n(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var r=document.querySelector('meta[name="stripe-publishable-key"]').content,o=document.querySelector('meta[name="stripe-account-id"]').content;new function e(t,r){var u=this;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),n(this,"setupStripe",(function(){return u.stripe=Stripe(u.key),u.stripeConnect&&(u.stripe.stripeAccount=o),u})),n(this,"handle",(function(){var e={type:"sofort",amount:document.querySelector('meta[name="amount"]').content,currency:"eur",redirect:{return_url:document.querySelector('meta[name="return-url"]').content},sofort:{country:document.querySelector('meta[name="country"]').content}};document.getElementById("pay-now").addEventListener("click",(function(t){document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),u.stripe.createSource(e).then((function(e){if(e.hasOwnProperty("source"))return window.location=e.source.redirect.url;document.getElementById("pay-now").disabled=!1,document.querySelector("#pay-now > svg").classList.add("hidden"),document.querySelector("#pay-now > span").classList.remove("hidden"),this.errors.textContent="",this.errors.textContent=e.error.message,this.errors.hidden=!1,document.getElementById("pay-now").disabled=!1}))}))})),this.key=t,this.errors=document.getElementById("errors"),this.stripeConnect=r}(r,o).setupStripe().handle()}});

View File

@ -8,10 +8,10 @@
"/js/clients/payments/authorize-credit-card-payment.js": "/js/clients/payments/authorize-credit-card-payment.js?id=a376eff2227da398b0ba",
"/js/clients/payments/card-js.min.js": "/js/clients/payments/card-js.min.js?id=5469146cd629ea1b5c20",
"/js/clients/payments/checkout-credit-card.js": "/js/clients/payments/checkout-credit-card.js?id=98e406fa8e4db0e93427",
"/js/clients/payments/stripe-ach.js": "/js/clients/payments/stripe-ach.js?id=c4012ad90f17d60432ad",
"/js/clients/payments/stripe-alipay.js": "/js/clients/payments/stripe-alipay.js?id=6dbe9316b98deea55421",
"/js/clients/payments/stripe-credit-card.js": "/js/clients/payments/stripe-credit-card.js?id=c37c3892d35c50d82521",
"/js/clients/payments/stripe-sofort.js": "/js/clients/payments/stripe-sofort.js?id=9b9fd56d655ad238f149",
"/js/clients/payments/stripe-ach.js": "/js/clients/payments/stripe-ach.js?id=76d8ba6a814b3015e359",
"/js/clients/payments/stripe-alipay.js": "/js/clients/payments/stripe-alipay.js?id=7a4ce306366be98be5f7",
"/js/clients/payments/stripe-credit-card.js": "/js/clients/payments/stripe-credit-card.js?id=2c828298b04fe30a4fe7",
"/js/clients/payments/stripe-sofort.js": "/js/clients/payments/stripe-sofort.js?id=282f5d57f1c1efe8f896",
"/js/clients/quotes/action-selectors.js": "/js/clients/quotes/action-selectors.js?id=1b8f9325aa6e8595e7fa",
"/js/clients/quotes/approve.js": "/js/clients/quotes/approve.js?id=85bcae0a646882e56b12",
"/js/clients/shared/multiple-downloads.js": "/js/clients/shared/multiple-downloads.js?id=5c35d28cf0a3286e7c45",

View File

@ -14,11 +14,17 @@ class AuthorizeACH {
this.key = document.querySelector(
'meta[name="stripe-publishable-key"]'
).content;
this.stripe_connect = document.querySelector(
'meta[name="stripe-account-id"]'
).content;
}
setupStripe = () => {
this.stripe = Stripe(this.key);
if(this.stripe_connect)
this.stripe.stripeAccount = this.stripe_connect;
return this;
};

View File

@ -9,14 +9,19 @@
*/
class ProcessAlipay {
constructor(key) {
constructor(key, stripeConnect) {
this.key = key;
this.stripeConnect = stripeConnect;
this.errors = document.getElementById('errors');
}
setupStripe = () => {
this.stripe = Stripe(this.key);
if(this.stripeConnect)
this.stripe.stripeAccount = this.stripeConnect;
return this;
};
@ -57,4 +62,8 @@ const publishableKey = document.querySelector(
'meta[name="stripe-publishable-key"]'
).content;
new ProcessAlipay(publishableKey).setupStripe().handle();
const stripeConnect = document.querySelector(
'meta[name="stripe-account-id"]'
).content;
new ProcessAlipay(publishableKey, stripeConnect).setupStripe().handle();

View File

@ -9,14 +9,19 @@
*/
class StripeCreditCard {
constructor(key, secret, onlyAuthorization) {
constructor(key, secret, onlyAuthorization, stripeConnect) {
this.key = key;
this.secret = secret;
this.onlyAuthorization = onlyAuthorization;
this.stripeConnect = stripeConnect;
}
setupStripe() {
this.stripe = Stripe(this.key);
if(this.stripeConnect)
this.stripe.stripeAccount = this.stripeConnect;
this.elements = this.stripe.elements();
return this;
@ -201,4 +206,7 @@ const secret =
const onlyAuthorization =
document.querySelector('meta[name="only-authorization"]').content ?? '';
new StripeCreditCard(publishableKey, secret, onlyAuthorization).handle();
const stripeConnect =
document.querySelector('meta[name="stripe-account-id"]').content;
new StripeCreditCard(publishableKey, secret, onlyAuthorization, stripeConnect).handle();

View File

@ -9,14 +9,18 @@
*/
class ProcessSOFORT {
constructor(key) {
constructor(key, stripeConnect) {
this.key = key;
this.errors = document.getElementById('errors');
this.stripeConnect = stripeConnect;
}
setupStripe = () => {
this.stripe = Stripe(this.key);
if(this.stripeConnect)
this.stripe.stripeAccount = stripeConnect;
return this;
};
@ -62,4 +66,7 @@ const publishableKey = document.querySelector(
'meta[name="stripe-publishable-key"]'
).content;
new ProcessSOFORT(publishableKey).setupStripe().handle();
const stripeConnect =
document.querySelector('meta[name="stripe-account-id"]').content;
new ProcessSOFORT(publishableKey, stripeConnect).setupStripe().handle();

View File

@ -2,6 +2,7 @@
@section('gateway_head')
<meta name="stripe-publishable-key" content="{{ $gateway->company_gateway->getPublishableKey() }}">
<meta name="stripe-account-id" content="{{ $gateway->company_gateway->getConfigField('account_id') }}">
@endsection
@section('gateway_content')

View File

@ -2,6 +2,7 @@
@section('gateway_head')
<meta name="stripe-publishable-key" content="{{ $gateway->getPublishableKey() }}">
<meta name="stripe-account-id" content="{{ $gateway->company_gateway->getConfigField('account_id') }}">
<meta name="return-url" content="{{ $return_url }}">
<meta name="currency" content="{{ $currency }}">
<meta name="amount" content="{{ $stripe_amount }}">

View File

@ -0,0 +1,13 @@
@extends('portal.ninja2020.layout.clean')
@section('meta_title', ctrans('texts.success'))
@section('body')
<div class="flex flex-col justify-center items-center mt-10">
<div class="mb-4">
<svg height="60" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" x="0" y="0" viewBox="0 0 468 222.5" xml:space="preserve"><style>.st0{fill-rule:evenodd;clip-rule:evenodd;fill:#635bff}</style><path class="st0" d="M414 113.4c0-25.6-12.4-45.8-36.1-45.8-23.8 0-38.2 20.2-38.2 45.6 0 30.1 17 45.3 41.4 45.3 11.9 0 20.9-2.7 27.7-6.5v-20c-6.8 3.4-14.6 5.5-24.5 5.5-9.7 0-18.3-3.4-19.4-15.2h48.9c0-1.3.2-6.5.2-8.9zm-49.4-9.5c0-11.3 6.9-16 13.2-16 6.1 0 12.6 4.7 12.6 16h-25.8zM301.1 67.6c-9.8 0-16.1 4.6-19.6 7.8l-1.3-6.2h-22v116.6l25-5.3.1-28.3c3.6 2.6 8.9 6.3 17.7 6.3 17.9 0 34.2-14.4 34.2-46.1-.1-29-16.6-44.8-34.1-44.8zm-6 68.9c-5.9 0-9.4-2.1-11.8-4.7l-.1-37.1c2.6-2.9 6.2-4.9 11.9-4.9 9.1 0 15.4 10.2 15.4 23.3 0 13.4-6.2 23.4-15.4 23.4zM223.8 61.7l25.1-5.4V36l-25.1 5.3zM223.8 69.3h25.1v87.5h-25.1zM196.9 76.7l-1.6-7.4h-21.6v87.5h25V97.5c5.9-7.7 15.9-6.3 19-5.2v-23c-3.2-1.2-14.9-3.4-20.8 7.4zM146.9 47.6l-24.4 5.2-.1 80.1c0 14.8 11.1 25.7 25.9 25.7 8.2 0 14.2-1.5 17.5-3.3V135c-3.2 1.3-19 5.9-19-8.9V90.6h19V69.3h-19l.1-21.7zM79.3 94.7c0-3.9 3.2-5.4 8.5-5.4 7.6 0 17.2 2.3 24.8 6.4V72.2c-8.3-3.3-16.5-4.6-24.8-4.6C67.5 67.6 54 78.2 54 95.9c0 27.6 38 23.2 38 35.1 0 4.6-4 6.1-9.6 6.1-8.3 0-18.9-3.4-27.3-8v23.8c9.3 4 18.7 5.7 27.3 5.7 20.8 0 35.1-10.3 35.1-28.2-.1-29.8-38.2-24.5-38.2-35.7z"/></svg>
</div>
<p>Connecting your account using Stripe has been successfully completed.</p>
<span>Click <a class="font-semibold hover:underline" href="{{ url('/#/settings/company_gateways') }}">here</a> to continue.</span>
</div>
@endsection

View File

@ -2,6 +2,7 @@
@section('gateway_head')
<meta name="stripe-publishable-key" content="{{ $gateway->getPublishableKey() }}">
<meta name="stripe-account-id" content="{{ $gateway->company_gateway->getConfigField('account_id') }}">
<meta name="stripe-secret" content="{{ $intent->client_secret }}">
<meta name="only-authorization" content="true">
<meta name="stripe-token" content="">

View File

@ -2,6 +2,7 @@
@section('gateway_head')
<meta name="stripe-publishable-key" content="{{ $gateway->getPublishableKey() }}">
<meta name="stripe-account-id" content="{{ $gateway->company_gateway->getConfigField('account_id') }}">
<meta name="stripe-secret" content="{{ $intent->client_secret }}">
<meta name="only-authorization" content="">
@endsection

View File

@ -2,6 +2,7 @@
@section('gateway_head')
<meta name="stripe-publishable-key" content="{{ $gateway->getPublishableKey() }}">
<meta name="stripe-account-id" content="{{ $gateway->company_gateway->getConfigField('account_id') }}">
<meta name="return-url" content="{{ $return_url }}">
<meta name="amount" content="{{ $stripe_amount }}">
<meta name="country" content="{{ $country }}">

View File

@ -190,6 +190,14 @@ Route::match(['get', 'post'], 'payment_webhook/{company_key}/{company_gateway_id
->name('payment_webhook');
Route::post('api/v1/postmark_webhook', 'PostMarkController@webhook');
Route::get('token_hash_router', 'OneTimeTokenController@router');
Route::get('webcron', 'WebCronController@index');
Route::group(['middleware' => ['api_db', 'locale']], function () {
Route::get('stripe_connect/completed', 'StripeConnectController@completed')->name('stripe_connect.return');
Route::get('stripe_connect/{token}', 'StripeConnectController@initialize')->name('stripe_connect.initialization');
});
Route::fallback('BaseController@notFound');