frontend & backend update

This commit is contained in:
carodej
2020-06-19 08:03:29 +02:00
parent 95bc310def
commit a2cab6198e
83 changed files with 4464 additions and 1907 deletions

View File

@@ -5,6 +5,8 @@ namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Resources\GatewayCollection;
use App\Http\Resources\GatewayResource;
use App\Http\Resources\InvoiceCollection;
use App\Invoice;
use App\PaymentGateway;
use Illuminate\Http\Request;
@@ -33,6 +35,19 @@ class GatewayController extends Controller
return new GatewayResource($gateway);
}
/**
* Get single payment gateway by slug
*
* @param $slug
* @return InvoiceCollection
*/
public function show_transactions($slug)
{
return new InvoiceCollection(
Invoice::where('provider', $slug)->get()
);
}
/**
* Update payment gateway options
*

View File

@@ -8,12 +8,21 @@ use App\Http\Resources\PlanResource;
use App\Http\Resources\UserResource;
use App\Http\Resources\UsersCollection;
use App\Plan;
use App\Services\StripeService;
use App\User;
use Illuminate\Http\Request;
use Rinvex\Subscriptions\Models\PlanFeature;
class PlanController extends Controller
{
/**
* PlanController constructor.
*/
public function __construct(StripeService $stripe)
{
$this->stripe = $stripe;
}
/**
* Get all plans
*
@@ -22,7 +31,7 @@ class PlanController extends Controller
public function index()
{
return new PlanCollection(
app('rinvex.subscriptions.plan')->all()
$this->stripe->getPlans()
);
}
@@ -35,7 +44,7 @@ class PlanController extends Controller
public function show($id)
{
return new PlanResource(
app('rinvex.subscriptions.plan')->find($id)
$this->stripe->getPlan($id)
);
}
@@ -47,26 +56,9 @@ class PlanController extends Controller
*/
public function store(Request $request)
{
$plan = app('rinvex.subscriptions.plan')->create([
'description' => $request->input('attributes.description'),
'price' => $request->input('attributes.price'),
'name' => $request->input('attributes.name'),
'currency' => 'USD',
'invoice_period' => 1,
'sort_order' => 1,
'signup_fee' => 0,
]);
// Create multiple plan features at once
$plan->features()->saveMany([
new PlanFeature([
'name' => 'Storage capacity',
'value' => $request->input('attributes.capacity'),
'sort_order' => 1
]),
]);
return new PlanResource($plan);
return new PlanResource(
$this->stripe->createPlan($request)
);
}
/**
@@ -78,18 +70,24 @@ class PlanController extends Controller
*/
public function update(Request $request, $id)
{
// TODO: validation request
$plan = app('rinvex.subscriptions.plan')->find($id);
if ($request->name === 'capacity') {
$plan->getFeatureBySlug('storage-capacity')->update(['value' => $request->value]);
} else {
$plan->update(make_single_input($request));
}
$this->stripe->updatePlan($request, $id);
return response('Saved!', 204);
}
/**
* Delete plan
*
* @param $id
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response
*/
public function delete($id)
{
$this->stripe->deletePlan($id);
return response('Done!', 204);
}
/**
* Get subscriptions
*

View File

@@ -258,11 +258,6 @@ class EditItemsController extends Controller
return Demo::upload($request);
}
// Check if user can upload
if (config('vuefilemanager.limit_storage_by_capacity') && user_storage_percentage() >= 100) {
abort(423, 'You exceed your storage limit!');
}
// Check permission to upload for authenticated editor
if ($request->user()->tokenCan('editor')) {

View File

@@ -4,19 +4,32 @@ namespace App\Http\Controllers\General;
use App\Http\Controllers\Controller;
use App\Http\Resources\PricingCollection;
use App\Services\StripeService;
use Illuminate\Http\Request;
class PricingController extends Controller
{
/**
* PlanController constructor.
*/
public function __construct(StripeService $stripe)
{
$this->stripe = $stripe;
}
/**
* Get all active plans
*
* @return PricingCollection
*/
public function index() {
return new PricingCollection(
app('rinvex.subscriptions.plan')->where('is_active', 1)->get()
public function index()
{
$collection = new PricingCollection(
$this->stripe->getActivePlans()
);
return $collection->sortBy('product.metadata.capacity')
->values()
->all();
}
}

View File

@@ -0,0 +1,107 @@
<?php
namespace App\Http\Controllers\User;
use App\Http\Controllers\Controller;
use App\Http\Resources\PaymentCardCollection;
use App\Http\Resources\PaymentCardResource;
use App\Http\Resources\PaymentDefaultCardResource;
use App\Services\PaymentService;
use App\UserCard;
use Auth;
use Illuminate\Http\Request;
use Laravel\Cashier\PaymentMethod;
class PaymentCardsController extends Controller
{
/**
* @var PaymentService
*/
private $payment;
/**
* PaymentCardsController constructor.
*/
public function __construct(PaymentService $payment)
{
$this->payment = $payment;
}
/**
* Update card detail
*
* @param Request $request
* @param $id
*/
public function update(Request $request, $id)
{
$user = Auth::user();
// Update DefaultPayment Method
$user->updateDefaultPaymentMethod($id);
// Sync default payment method
$user->updateDefaultPaymentMethodFromStripe();
}
/**
* Delete user credit card
*
*/
public function delete($id)
{
$user = Auth::user();
// Get payment method
$paymentMethod = $user->findPaymentMethod($id);
// Delete payment method
$paymentMethod->delete();
// Sync default payment method
$user->updateDefaultPaymentMethodFromStripe();
return response('Done!', 204);
}
/**
* Get user payment methods sorted by default and others
*
* @return array
*/
public function payment_methods()
{
$user = Auth::user();
// Get default payment method
$defaultPaymentMethodObject = $user->defaultPaymentMethod();
$defaultPaymentMethod = $defaultPaymentMethodObject instanceof PaymentMethod
? $defaultPaymentMethodObject->asStripePaymentMethod()
: $defaultPaymentMethodObject;
// filter payment methods without default payment
$paymentMethods = $user->paymentMethods()->filter(function ($paymentMethod) use ($defaultPaymentMethod) {
return $paymentMethod->id !== $defaultPaymentMethod->id;
});
// Get payment methods
$paymentMethodsMapped = $paymentMethods->map(function ($paymentMethod) {
return $paymentMethod->asStripePaymentMethod();
})->values()->all();
if (is_null($paymentMethodsMapped) && is_null($paymentMethodsMapped)) {
return [
'default' => null,
'others' => [],
];
}
return [
'default' => $defaultPaymentMethod instanceof PaymentMethod
? new PaymentCardResource($defaultPaymentMethod)
: new PaymentDefaultCardResource($defaultPaymentMethod),
'others' => new PaymentCardCollection($paymentMethodsMapped),
];
}
}

View File

@@ -3,53 +3,82 @@
namespace App\Http\Controllers\User;
use App\Http\Controllers\Controller;
use App\Http\Requests\Subscription\StoreUpgradeAccountRequest;
use App\Http\Resources\UserSubscription;
use App\Invoice;
use App\Services\PaymentService;
use App\Services\StripeService;
use Auth;
use Cartalyst\Stripe\Exception\CardErrorException;
use Illuminate\Contracts\Routing\ResponseFactory;
use Illuminate\Http\Request;
use Laravel\Cashier\Exceptions\IncompletePayment;
use Symfony\Component\HttpKernel\Exception\HttpException;
class SubscriptionController extends Controller
{
private $stripe;
/**
* SubscriptionController constructor.
* @param $payment
*/
public function __construct(StripeService $stripe)
{
$this->stripe = $stripe;
}
/**
* Generate setup intent
*
* @return \Stripe\SetupIntent
*/
public function stripe_setup_intent()
{
// Get user
$user = Auth::user();
// Create stripe customer if not exist
$user->createOrGetStripeCustomer();
// Return setup intent
return $user->createSetupIntent();
}
public function show()
{
return new UserSubscription(
Auth::user()
);
}
/**
* Upgrade account to subscription
*
* @param Request $request
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response
* @param StoreUpgradeAccountRequest $request
* @return ResponseFactory|\Illuminate\Http\Response
*/
public function upgrade(Request $request)
public function upgrade(StoreUpgradeAccountRequest $request)
{
// TODO: validation request
// Get user
$user = Auth::user();
// Get requested plan
$plan = app('rinvex.subscriptions.plan')
->find($request->input('plan.data.id'));
$plan = $this->stripe->getPlan($request->input('plan.data.id'));
// Check if user have subscription
if ($user->activeSubscriptions()->count() !== 0) {
// Set user billing
$user->setBilling($request->input('billing'));
// Get old subscription
$subscription = $user->subscription('main');
// Change subscription plan
$subscription->changePlan($plan);
} else {
// Create subscription
$user->newSubscription('main', $plan);
}
// Make subscription
$this->stripe->createOrReplaceSubscription($request, $user);
// Update user storage limit
$user->settings()->update([
'storage_capacity' => $plan->features->first()->value
'storage_capacity' => $plan['product']['metadata']['capacity']
]);
// Store invoice
Invoice::create(
get_invoice_data($user, $plan)
);
Invoice::create(get_invoice_data($user, $plan, 'stripe'));
return response('Done!', 204);
}
@@ -57,15 +86,25 @@ class SubscriptionController extends Controller
/**
* Cancel Subscription
*
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response
* @return ResponseFactory|\Illuminate\Http\Response
*/
public function cancel() {
// Get user
$user = Auth::user();
public function cancel()
{
// Cancel subscription
$user->subscription('main')->cancel();
Auth::user()->subscription('main')->cancel();
return response('Done!', 204);
}
/**
* Resume Subscription
*
* @return ResponseFactory|\Illuminate\Http\Response
*/
public function resume()
{
// Resume subscription
Auth::user()->subscription('main')->resume();
return response('Done!', 204);
}

View File

@@ -27,7 +27,14 @@ function get_invoice_number()
}
}
function get_invoice_data($user, $plan)
/**
* Get data to render in invoice tempalte
* @param $user
* @param $plan
* @param $provider
* @return array
*/
function get_invoice_data($user, $plan, $provider)
{
$subscription = $user->subscription('main');
$order_number = get_invoice_number();
@@ -36,15 +43,17 @@ function get_invoice_data($user, $plan)
return [
'token' => $token,
'order' => $order_number,
'provider' => $provider,
'user_id' => $user->id,
'plan_id' => $plan->id,
'total' => $plan->price,
'plan_id' => $plan['plan']['id'],
'total' => $plan['plan']['amount'],
'currency' => 'USD',
'bag' => [
[
'description' => 'Subscription - ' . $plan->name,
'date' => format_date($subscription->starts_at, '%d. %B. %Y') . ' - ' . format_date($subscription->ends_at, '%d. %B. %Y'),
'amount' => $plan->price,
'description' => 'Subscription - ' . $plan['product']['name'],
//'date' => format_date($subscription->starts_at, '%d. %B. %Y') . ' - ' . format_date($subscription->ends_at, '%d. %B. %Y'),
'date' => format_date(Carbon::now(),'%d. %B. %Y'),
'amount' => $plan['plan']['amount'],
]
],
'seller' => [
@@ -282,15 +291,21 @@ function get_storage_fill_percentage($used, $capacity)
}
/**
* Get user capacity fill percentage
* Get user capacity fill by percentage
*
* @return string
*/
function user_storage_percentage()
function user_storage_percentage($id, $additionals = null)
{
$user = Auth::user();
$user = \App\User::findOrFail($id);
return get_storage_fill_percentage($user->used_capacity, $user->settings->storage_capacity);
$used = $user->used_capacity;
if ($additionals) {
$used = $user->used_capacity + $additionals;
}
return get_storage_fill_percentage($used, $user->settings->storage_capacity);
}
/**

View File

@@ -0,0 +1,23 @@
<?php
/**
* Check if current user subscribed plan is highest
*
* @param $id
* @param $subscribed_capacity
* @return int
*/
function is_highest_plan($plan)
{
$plans = app('rinvex.subscriptions.plan')->all();
$unsubscribed = $plans->filter(function ($item) use ($plan) {
return $item->id !== $plan->id;
});
$capacities = $unsubscribed->map(function ($item) {
return $item->features->first()->value;
});
return max(Arr::flatten($capacities)) < $plan->features->first()->value ? 1 : 0;
}

View File

@@ -0,0 +1,56 @@
<?php
namespace App\Http\Requests\Subscription;
use Illuminate\Foundation\Http\FormRequest;
class StoreUpgradeAccountRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
// Billings
'billing' => 'required|array',
'billing.billing_address' => 'required|string',
'billing.billing_city' => 'required|string',
'billing.billing_country' => 'required|string',
'billing.billing_name' => 'required|string',
'billing.billing_phone_number' => 'required|string',
'billing.billing_postal_code' => 'required|string',
'billing.billing_state' => 'required|string',
// Payment
'payment' => 'required|array',
'payment.type' => 'required|string',
'payment.meta' => 'required|sometimes|array',
'payment.meta.pm' => 'required|sometimes|string',
// Plan
'plan.data' => 'required|array',
'plan.data.attributes' => 'required|array',
'plan.data.attributes.capacity' => 'required|digits_between:1,9',
'plan.data.attributes.capacity_formatted' => 'required|string',
'plan.data.attributes.currency' => 'required|string',
'plan.data.attributes.description' => 'required|string',
'plan.data.attributes.name' => 'required|string',
'plan.data.attributes.price' => 'required|string',
'plan.data.id' => 'required|string',
'plan.data.type' => 'required|string',
];
}
}

View File

@@ -19,15 +19,16 @@ class GatewayResource extends JsonResource
'id' => (string)$this->id,
'type' => 'gateways',
'attributes' => [
'status' => $this->status,
'sandbox' => $this->sandbox,
'name' => $this->name,
'slug' => $this->slug,
'logo' => $this->logo,
'client_id' => $this->client_id,
'secret' => $this->secret,
'webhook' => $this->webhook,
'optional' => $this->optional,
'status' => $this->status,
'sandbox' => $this->sandbox,
'name' => $this->name,
'slug' => $this->slug,
'logo' => $this->logo,
'client_id' => $this->client_id,
'secret' => $this->secret,
'webhook' => $this->webhook,
'payment_processed' => $this->payment_processed,
'optional' => $this->optional,
]
]
];

View File

@@ -0,0 +1,23 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class PaymentCardCollection extends ResourceCollection
{
public $collects = PaymentCardResource::class;
/**
* Transform the resource collection into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'data' => $this->collection,
];
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class PaymentCardResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'data' => [
'id' => (string)$this['id'],
'type' => 'payment_method',
'attributes' => [
'provider' => 'stripe',
'card_id' => $this['id'],
'brand' => strtolower($this['card']['brand']),
'last4' => $this['card']['last4'],
'exp_month' => $this['card']['exp_month'],
'exp_year' => $this['card']['exp_year'],
'created_at' => format_date($this['created_at'], '%d. %B. %Y'),
'status' => 'active',
'default' => 0,
]
]
];
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class PaymentDefaultCardResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'data' => [
'id' => (string)$this['id'],
'type' => 'payment_method',
'attributes' => [
'provider' => 'stripe',
'card_id' => $this['id'],
'brand' => isset($this['brand']) ? strtolower($this['brand']) : strtolower($this['card']['brand']),
'last4' => isset($this['last4']) ? $this['last4'] : $this['card']['last4'],
'exp_month' => isset($this['exp_month']) ? $this['exp_month'] : $this['card']['exp_month'],
'exp_year' => isset($this['exp_year']) ? $this['exp_year'] : $this['card']['exp_year'],
'created_at' => format_date($this['created_at'], '%d. %B. %Y'),
'status' => 'active',
'default' => 0,
]
]
];
}
}

View File

@@ -16,19 +16,18 @@ class PlanResource extends JsonResource
{
return [
'data' => [
'id' => (string)$this->id,
'id' => $this['plan']['id'],
'type' => 'plans',
'attributes' => [
'subscribers' => $this->subscriptions->count(),
'status' => $this->is_active,
'name' => $this->name,
'description' => $this->description,
'price' => $this->price,
'capacity_formatted' => format_gigabytes($this->features->first()->value),
'capacity' => $this->features->first()->value,
'created_at_formatted' => format_date($this->created_at),
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
'subscribers' => $this['plan']['aggregate_usage'],
'status' => $this['plan']['active'],
'name' => $this['product']['name'],
'description' => $this['product']['description'],
'price' => $this['plan']['amount'],
'capacity_formatted' => format_gigabytes($this['product']['metadata']['capacity']),
'capacity' => $this['product']['metadata']['capacity'],
'created_at_formatted' => format_date($this['plan']['created']),
'created_at' => $this['plan']['created'],
]
]
];

View File

@@ -3,28 +3,29 @@
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
use Laravel\Cashier\Cashier;
class PricingResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'data' => [
'id' => (string)$this->id,
'id' => $this['plan']['id'],
'type' => 'plans',
'attributes' => [
'name' => $this->name,
'description' => $this->description,
'price' => $this->price,
'capacity_formatted' => format_gigabytes($this->features->first()->value),
'capacity' => (int) $this->features->first()->value,
'currency' => 'USD',
'name' => $this['product']['name'],
'description' => $this['product']['description'],
'price' => Cashier::formatAmount($this['plan']['amount']),
'capacity_formatted' => format_gigabytes($this['product']['metadata']['capacity']),
'capacity' => (int) $this['product']['metadata']['capacity'],
'currency' => 'USD',
]
]
];

View File

@@ -2,6 +2,9 @@
namespace App\Http\Resources;
use App\Services\StripeService;
use App\User;
use Cartalyst\Stripe\Api\PaymentMethods;
use Faker\Factory;
use Illuminate\Http\Resources\Json\JsonResource;
@@ -27,16 +30,14 @@ class UserResource extends JsonResource
'email' => env('APP_DEMO') ? $faker->email : $this->email,
'avatar' => $this->avatar,
'role' => $this->role,
'subscription' => $this->subscribed('main'),
'created_at_formatted' => format_date($this->created_at, '%d. %B. %Y'),
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
]
],
'relationships' => [
'subscription' => $this->activeSubscriptions()->count() !== 0
? new UserSubscription($this->subscription('main'))
: null,
'settings' => [
'settings' => [
'data' => [
'id' => (string)$this->settings->id,
'type' => 'settings',
@@ -51,14 +52,14 @@ class UserResource extends JsonResource
]
]
],
'storage' => [
'storage' => [
'data' => [
'id' => '1',
'type' => 'storage',
'attributes' => $this->storage
]
],
'favourites' => [
'favourites' => [
'data' => [
'id' => '1',
'type' => 'folders_favourite',
@@ -67,7 +68,7 @@ class UserResource extends JsonResource
],
],
],
'tree' => [
'tree' => [
'data' => [
'id' => '1',
'type' => 'folders_tree',
@@ -76,6 +77,7 @@ class UserResource extends JsonResource
],
],
],
'payment_methods' => new PaymentCardCollection($this->payment_cards)
]
];
}

View File

@@ -14,21 +14,33 @@ class UserSubscription extends JsonResource
*/
public function toArray($request)
{
$stripe = resolve('App\Services\StripeService');
$active_subscription = $this->subscription('main')->asStripeSubscription();
// Get subscription details
$subscription = $stripe->getPlan($this->subscription('main')->stripe_plan);
// Retrieve the timestamp from Stripe
$current_period_end = $active_subscription["current_period_end"];
$current_period_start = $active_subscription["current_period_start"];
$canceled_at = $active_subscription["canceled_at"];
return [
'data' => [
'id' => $this->id,
'id' => $subscription['plan']['id'],
'type' => 'subscription',
'attributes' => [
'active' => $this->active(),
'canceled' => $this->canceled(),
'name' => $this->plan->name,
'capacity' => (int) $this->plan->features->first()->value,
'capacity_formatted' => format_gigabytes($this->plan->features->first()->value),
'slug' => $this->slug,
'canceled_at' => format_date($this->created_at, '%d. %B. %Y'),
'created_at' => format_date($this->created_at, '%d. %B. %Y'),
'starts_at' => format_date($this->starts_at, '%d. %B. %Y'),
'ends_at' => format_date($this->ends_at, '%d. %B. %Y'),
/*'is_highest' => is_highest_plan($this->plan),*/
'active' => $subscription['plan']['active'],
'canceled' => $this->subscription('main')->cancelled(),
'name' => $subscription['product']['name'],
'capacity' => (int)$subscription['product']['metadata']['capacity'],
'capacity_formatted' => format_gigabytes($subscription['product']['metadata']['capacity']),
'slug' => $subscription['plan']['id'],
'canceled_at' => format_date($canceled_at, '%d. %B. %Y'),
'created_at' => format_date($current_period_start, '%d. %B. %Y'),
'ends_at' => format_date($current_period_end, '%d. %B. %Y'),
]
]
];

View File

@@ -197,14 +197,20 @@ class Editor
*/
public static function upload($request, $shared = null)
{
// Get user data
$user_scope = is_null($shared) ? $request->user()->token()->scopes[0] : 'editor';
$user_id = is_null($shared) ? Auth::id() : $shared->user_id;
// Get parent_id from request
$folder_id = $request->parent_id === 0 ? 0 : $request->parent_id;
$file = $request->file('file');
// Get user data
$user_scope = is_null($shared) ? $request->user()->token()->scopes[0] : 'editor';
$user_id = is_null($shared) ? Auth::id() : $shared->user_id;
$user_storage_used = user_storage_percentage($user_id, $file->getSize());
// Check if user can upload
if (config('vuefilemanager.limit_storage_by_capacity') && $user_storage_used >= 100) {
abort(423, 'You exceed your storage limit!');
}
// File
$filename = Str::random() . '-' . str_replace(' ', '', $file->getClientOriginalName());
$filetype = get_file_type($file);

View File

@@ -4,6 +4,43 @@ namespace App;
use Illuminate\Database\Eloquent\Model;
/**
* App\Invoice
*
* @property int $id
* @property string $token
* @property string $order
* @property string|null $provider
* @property string $user_id
* @property string $plan_id
* @property array $seller
* @property array $client
* @property array $bag
* @property string|null $notes
* @property string $total
* @property string $currency
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \App\User|null $user
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice query()
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice whereBag($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice whereClient($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice whereCurrency($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice whereNotes($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice whereOrder($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice wherePlanId($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice whereProvider($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice whereSeller($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice whereToken($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice whereTotal($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice whereUserId($value)
* @mixin \Eloquent
*/
class Invoice extends Model
{
protected $guarded = [

View File

@@ -4,6 +4,36 @@ namespace App;
use Illuminate\Database\Eloquent\Model;
/**
* App\PaymentGateway
*
* @property int $id
* @property int $status
* @property int $sandbox
* @property string $name
* @property string $slug
* @property string $logo
* @property string|null $client_id
* @property string|null $secret
* @property string|null $webhook
* @property string|null $optional
* @property int|null $payment_processed
* @method static \Illuminate\Database\Eloquent\Builder|\App\PaymentGateway newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|\App\PaymentGateway newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|\App\PaymentGateway query()
* @method static \Illuminate\Database\Eloquent\Builder|\App\PaymentGateway whereClientId($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\PaymentGateway whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\PaymentGateway whereLogo($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\PaymentGateway whereName($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\PaymentGateway whereOptional($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\PaymentGateway wherePaymentProcessed($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\PaymentGateway whereSandbox($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\PaymentGateway whereSecret($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\PaymentGateway whereSlug($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\PaymentGateway whereStatus($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\PaymentGateway whereWebhook($value)
* @mixin \Eloquent
*/
class PaymentGateway extends Model
{
protected $guarded = ['id'];

View File

@@ -0,0 +1,199 @@
<?php
namespace App\Services;
use App\PaymentGateway;
use App\User;
use App\UserCard;
use App\UserSettings;
use Cartalyst\Stripe\Exception\CardErrorException;
use Stripe;
use Symfony\Component\HttpKernel\Exception\HttpException;
class PaymentService
{
/**
* PaymentService constructor.
*/
public function __construct()
{
$this->stripe = Stripe::make(config('services.stripe.secret'), '2020-03-02');
}
/**
* Get and charge customer
*
* @param $request
* @param $plan
* @param $user
*/
public function getAndChargeCustomer($request, $plan, $user)
{
// Get Stripe Customer
$customer = $this->findOrCreateStripeCustomer($user);
$paymentIntent = $this->stripe->paymentIntents()->create([
'amount' => $plan->price,
'currency' => 'USD',
'payment_method_types' => [
'card',
],
]);
dd($paymentIntent);
// Try charge customer
try {
if ($request->has('payment.meta.card_token')) {
// Register customer credit card
$created_card = $this->registerStripeCreditCard($customer, $request->input('payment.meta.card_token'));
// Charge with created credit card
/*$this->stripe->charges()->create([
'description' => 'Subscription ' . $plan->name,
'customer' => $customer['id'],
'amount' => $plan->price,
'currency' => 'USD',
'source' => $created_card->card_id
]);*/
} else {
// Charge with customer default creadit card
$this->stripe->charges()->create([
'description' => 'Subscription ' . $plan->name,
'customer' => $customer['id'],
'amount' => $plan->price,
'currency' => 'USD',
]);
}
} catch (CardErrorException $error) {
//dd($error);
// Remove previously registered card
if (in_array($error->getErrorCode(), ['rejected_card', 'card_declined'])
&& $request->has('payment.meta.card_token')
&& isset($error->getRawOutput()['error']['charge'])) {
// Get charge
$charge = $this->stripe->charges()->find($error->getRawOutput()['error']['charge']);
// Remove card from stripe
$this->deleteStripeCard($user->settings->stripe_customer_id, $charge['payment_method']);
// Get card
$error_card = UserCard::where('card_id', $charge['payment_method'])->first();
// Delete card
$error_card->delete();
}
throw new HttpException($error->getCode(), $error->getMessage());
}
// Increase payment processed column
PaymentGateway::where('slug', 'stripe')->first()->increment('payment_processed');
}
/**
* Find or create stripe customer
*
* @param $user
* @return array
*/
private function findOrCreateStripeCustomer($user)
{
// Get existing stripe customer
if ($user->settings->stripe_customer_id) {
return $this->stripe->customers()->find($user->settings->stripe_customer_id);
}
// Create new stripe costumer
if (!$user->settings->stripe_customer_id) {
$customer = $this->stripe->customers()->create([
'email' => $user->email,
'name' => $user->name,
'address' => [
'city' => $user->settings->billing_city,
'country' => $user->settings->billing_country,
'line1' => $user->settings->billing_address,
'state' => $user->settings->billing_state,
'postal_code' => $user->settings->billing_postal_code,
]
]);
// Store user stripe_customer_id
$user->settings()->update([
'stripe_customer_id' => $customer['id']
]);
return $customer;
}
}
/**
* Register stripe credit card
*
* @param $customer
* @param $card_id
* @return object
*/
private function registerStripeCreditCard($customer, $card_id)
{
// Register user card
$card = $this->stripe->cards()->create($customer['id'], $card_id);
// Get user settings
$user_settings = UserSettings::where('stripe_customer_id', $customer['id'])->first();
// Set default status
$default_card = UserCard::where('user_id', $user_settings->user_id)->get()->count() > 0 ? 0 : 1;
// Store user card
$card = UserCard::create([
'user_id' => $user_settings->user_id,
'status' => 'active',
'default' => $default_card,
'provider' => 'stripe',
'card_id' => $card['id'],
'brand' => $card['brand'],
'last4' => $card['last4'],
'exp_month' => $card['exp_month'],
'exp_year' => $card['exp_year'],
]);
return $card;
}
/**
* Set new default source from existing credit card
*
* @param $stripe_customer_id
* @param $card_id
*/
public function setDefaultStripeCard($stripe_customer_id, $card_id)
{
$this->stripe->customers()->update($stripe_customer_id, [
'default_source' => $card_id,
]);
}
/**
* Delete customer stripe credit card
*
* @param $stripe_customer_id
* @param $card_id
*/
public function deleteStripeCard($stripe_customer_id, $card_id)
{
$this->stripe->cards()->delete($stripe_customer_id, $card_id);
}
}

View File

@@ -0,0 +1,229 @@
<?php
namespace App\Services;
use Illuminate\Support\Str;
use Laravel\Cashier\Exceptions\IncompletePayment;
use Stripe;
use Symfony\Component\HttpKernel\Exception\HttpException;
class StripeService
{
/**
* Stripe Service constructor.
*/
public function __construct()
{
$this->stripe = Stripe::make(env('STRIPE_SECRET'), '2020-03-02');
}
/**
* Get default payment option or set new default payment
*
* @param $request
* @param $user
* @return mixed
*/
public function getOrSetDefaultPaymentMethod($request, $user)
{
// Check payment method
if (! $request->has('payment.meta.pm') && $user->hasDefaultPaymentMethod()) {
// Get default payment
return $user->defaultPaymentMethod()->paymentMethod;
} else if ( $request->has('payment.meta.pm') && $user->hasDefaultPaymentMethod() ) {
// Set new payment
return $user->addPaymentMethod($request->input('payment.meta.pm'))->paymentMethod;
} else if ( $request->has('payment.meta.pm') && ! $user->hasDefaultPaymentMethod() ) {
// Set new payment
return $user->updateDefaultPaymentMethod($request->input('payment.meta.pm'))->paymentMethod;
} else {
throw new HttpException(400, 'Something went wrong.');
}
}
/**
* Create new subscription or replace by new subscription
*
* @param $request
* @param $user
* @param $paymentMethod
*/
public function createOrReplaceSubscription($request, $user)
{
try {
// Get payment method
$paymentMethod = $this->getOrSetDefaultPaymentMethod($request, $user);
// Check if user have subscription
if ($user->subscribed('main')) {
// Change subscription plan
$user->subscription('main')->skipTrial()->swap($request->input('plan.data.id'));
} else {
// Create subscription
$user->newSubscription('main', $request->input('plan.data.id'))->create($paymentMethod);
}
} catch (IncompletePayment $exception) {
throw new HttpException(400, 'We can\'t charge your card');
}
}
/**
* Create plan
*
* @param $request
* @return mixed
*/
public function createPlan($request)
{
$product = $this->stripe->products()->create([
'name' => $request->input('attributes.name'),
'description' => $request->input('attributes.description'),
'metadata' => [
'capacity' => $request->input('attributes.capacity')
]
]);
$plan = $this->stripe->plans()->create([
'id' => Str::slug($request->input('attributes.name')),
'amount' => $request->input('attributes.price'),
'currency' => 'USD',
'interval' => 'month',
'product' => $product['id'],
]);
return compact('plan', 'product');
}
/**
* Update plan
*
* @param $request
* @param $id
*/
public function updatePlan($request, $id)
{
$plan_colls = ['is_active', 'price'];
$product_colls = ['name', 'description', 'capacity'];
$plan = $this->stripe->plans()->find($id);
// Update product
if (in_array($request->name, $product_colls)) {
if ($request->name === 'capacity') {
$this->stripe->products()->update($plan['product'], ['metadata' => ['capacity' => $request->value]]);
}
if ($request->name === 'name') {
$this->stripe->products()->update($plan['product'], ['name' => $request->value]);
}
if ($request->name === 'description') {
$this->stripe->products()->update($plan['product'], ['description' => $request->value]);
}
}
// Update plan
if (in_array($request->name, $plan_colls)) {
if ($request->name === 'is_active') {
$this->stripe->plans()->update($id, ['active' => $request->value]);
}
}
}
/**
* Get plan details
*
* @param $id
* @return mixed
*/
public function getPlan($id)
{
$plan = $this->stripe->plans()->find($id);
$product = $this->stripe->products()->find($plan['product']);
return compact('plan', 'product');
}
/**
* Get all plans
*
* @return mixed
*/
public function getPlans()
{
// Get stripe plans
$stripe_plans = $this->stripe->plans()->all();
// Plans container
$plans = [];
foreach ($stripe_plans['data'] as $plan) {
// Get stripe product
$product = $this->stripe->products()->find($plan['product']);
// Push data to $plan container
array_push($plans, [
'plan' => $plan,
'product' => $product,
]);
}
return $plans;
}
/**
* Get all active plans
*
* @return mixed
*/
public function getActivePlans()
{
// Get stripe plans
$stripe_plans = $this->stripe->plans()->all();
// Plans container
$plans = [];
foreach ($stripe_plans['data'] as $plan) {
if ($plan['active']) {
// Get stripe product
$product = $this->stripe->products()->find($plan['product']);
// Push data to $plan container
array_push($plans, [
'plan' => $plan,
'product' => $product,
]);
}
}
return $plans;
}
/**
* Delete plan
*
* @param $slug
*/
public function deletePlan($slug)
{
$this->stripe->plans()->delete($slug);
}
}

View File

@@ -10,6 +10,7 @@ use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Notification;
use Laravel\Cashier\Billable;
use Laravel\Passport\HasApiTokens;
use Rinvex\Subscriptions\Traits\HasSubscriptions;
@@ -53,10 +54,31 @@ use Rinvex\Subscriptions\Traits\HasSubscriptions;
* @method static \Illuminate\Database\Eloquent\Builder|\App\User whereRememberToken($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\User whereUpdatedAt($value)
* @mixin \Eloquent
* @property string $role
* @property string|null $stripe_id
* @property string|null $card_brand
* @property string|null $card_last_four
* @property string|null $trial_ends_at
* @property-read \Illuminate\Database\Eloquent\Collection|\App\FileManagerFolder[] $favourite_folders
* @property-read int|null $favourite_folders_count
* @property-read mixed $folder_tree
* @property-read mixed $storage
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Invoice[] $invoices
* @property-read int|null $invoices_count
* @property-read \Illuminate\Database\Eloquent\Collection|\App\UserCard[] $payment_cards
* @property-read int|null $payment_cards_count
* @property-read \App\UserSettings|null $settings
* @property-read \Illuminate\Database\Eloquent\Collection|\Laravel\Cashier\Subscription[] $subscriptions
* @property-read int|null $subscriptions_count
* @method static \Illuminate\Database\Eloquent\Builder|\App\User whereCardBrand($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\User whereCardLastFour($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\User whereRole($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\User whereStripeId($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\User whereTrialEndsAt($value)
*/
class User extends Authenticatable
{
use HasApiTokens, Notifiable, HasSubscriptions;
use HasApiTokens, Notifiable, Billable;
/**
* The attributes that are mass assignable.
@@ -95,11 +117,11 @@ class User extends Authenticatable
*
* @return mixed
*/
public function getStorageAttribute() {
public function getStorageAttribute()
{
return [
'used' => (float) get_storage_fill_percentage($this->used_capacity, $this->settings->storage_capacity),
'capacity' => $this->settings->storage_capacity,
'used' => (float)get_storage_fill_percentage($this->used_capacity, $this->settings->storage_capacity),
'capacity' => $this->settings->storage_capacity,
'capacity_formatted' => format_gigabytes($this->settings->storage_capacity),
];
}
@@ -109,8 +131,8 @@ class User extends Authenticatable
*
* @return mixed
*/
public function getUsedCapacityAttribute() {
public function getUsedCapacityAttribute()
{
$user_capacity = $this->files_with_trashed->map(function ($item) {
return $item->getRawOriginal();
})->sum('filesize');
@@ -118,7 +140,8 @@ class User extends Authenticatable
return $user_capacity;
}
public function getFolderTreeAttribute() {
public function getFolderTreeAttribute()
{
return FileManagerFolder::with(['folders.shared', 'shared:token,id,item_id,permission,protected'])
->where('parent_id', 0)
->where('user_id', $this->id)
@@ -139,6 +162,24 @@ class User extends Authenticatable
return url('/assets/images/' . 'default-avatar.png');
}
/**
* Set user billing info
*
* @param $billing
*/
public function setBilling($billing)
{
$this->settings()->update([
'billing_address' => $billing['billing_address'],
'billing_city' => $billing['billing_city'],
'billing_country' => $billing['billing_country'],
'billing_name' => $billing['billing_name'],
'billing_phone_number' => $billing['billing_phone_number'],
'billing_postal_code' => $billing['billing_postal_code'],
'billing_state' => $billing['billing_state'],
]);
}
/**
* Send the password reset notification.
*
@@ -165,8 +206,8 @@ class User extends Authenticatable
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany|\Illuminate\Database\Query\Builder
*/
public function latest_uploads() {
public function latest_uploads()
{
return $this->hasMany(FileManagerFile::class)->with(['parent'])->orderBy('created_at', 'DESC')->take(40);
}
@@ -175,8 +216,8 @@ class User extends Authenticatable
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function files() {
public function files()
{
return $this->hasMany(FileManagerFile::class);
}
@@ -185,8 +226,8 @@ class User extends Authenticatable
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function files_with_trashed() {
public function files_with_trashed()
{
return $this->hasMany(FileManagerFile::class)->withTrashed();
}
@@ -195,8 +236,8 @@ class User extends Authenticatable
*
* @return \Illuminate\Database\Eloquent\Relations\HasOne
*/
public function settings() {
public function settings()
{
return $this->hasOne(UserSettings::class);
}
@@ -205,8 +246,18 @@ class User extends Authenticatable
*
* @return \Illuminate\Database\Eloquent\Relations\HasOne
*/
public function invoices() {
public function invoices()
{
return $this->hasMany(Invoice::class);
}
/**
* Get user payment cards
*
* @return \Illuminate\Database\Eloquent\Relations\HasOne
*/
public function payment_cards()
{
return $this->hasMany(UserCard::class);
}
}

48
app/UserCard.php Normal file
View File

@@ -0,0 +1,48 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
/**
* App\UserCard
*
* @property int $id
* @property int $user_id
* @property string $provider
* @property int $default
* @property string $status
* @property string $card_id
* @property string $brand
* @property string $last4
* @property string $exp_month
* @property string $exp_year
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \App\User|null $user
* @method static \Illuminate\Database\Eloquent\Builder|\App\UserCard newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|\App\UserCard newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|\App\UserCard query()
* @method static \Illuminate\Database\Eloquent\Builder|\App\UserCard whereBrand($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\UserCard whereCardId($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\UserCard whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\UserCard whereDefault($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\UserCard whereExpMonth($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\UserCard whereExpYear($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\UserCard whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\UserCard whereLast4($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\UserCard whereProvider($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\UserCard whereStatus($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\UserCard whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\UserCard whereUserId($value)
* @mixin \Eloquent
*/
class UserCard extends Model
{
protected $guarded = ['id'];
public function user() {
return $this->hasOne(User::class);
}
}