1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-10 21:22:58 +01:00

Merge pull request #6091 from turbo124/v5-develop

WePay
This commit is contained in:
David Bomba 2021-06-21 15:10:42 +10:00 committed by GitHub
commit 4c8a486461
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
58 changed files with 3041 additions and 411 deletions

View File

@ -78,6 +78,10 @@ class CheckData extends Command
protected $isValid = true;
protected $wrong_paid_to_dates = 0;
protected $wrong_balances = 0;
public function handle()
{
$database_connection = $this->option('database') ? $this->option('database') : 'Connected to Default DB';
@ -305,10 +309,10 @@ class CheckData extends Command
private function checkPaidToDates()
{
$wrong_paid_to_dates = 0;
$this->wrong_paid_to_dates = 0;
$credit_total_applied = 0;
Client::withTrashed()->where('is_deleted', 0)->cursor()->each(function ($client) use ($wrong_paid_to_dates, $credit_total_applied) {
Client::withTrashed()->where('is_deleted', 0)->cursor()->each(function ($client) use ($credit_total_applied) {
$total_invoice_payments = 0;
foreach ($client->invoices()->where('is_deleted', false)->where('status_id', '>', 1)->get() as $invoice) {
@ -330,7 +334,7 @@ class CheckData extends Command
if (round($total_invoice_payments, 2) != round($client->paid_to_date, 2)) {
$wrong_paid_to_dates++;
$this->wrong_paid_to_dates++;
$this->logMessage($client->present()->name.' id = # '.$client->id." - Paid to date does not match Client Paid To Date = {$client->paid_to_date} - Invoice Payments = {$total_invoice_payments}");
@ -338,17 +342,16 @@ class CheckData extends Command
}
});
$this->logMessage("{$wrong_paid_to_dates} clients with incorrect paid to dates");
$this->logMessage("{$this->wrong_paid_to_dates} clients with incorrect paid to dates");
}
private function checkInvoicePayments()
{
$wrong_balances = 0;
$wrong_paid_to_dates = 0;
$this->wrong_balances = 0;
Client::cursor()->where('is_deleted', 0)->each(function ($client) use ($wrong_balances) {
Client::cursor()->where('is_deleted', 0)->each(function ($client) {
$client->invoices->where('is_deleted', false)->whereIn('status_id', '!=', Invoice::STATUS_DRAFT)->each(function ($invoice) use ($wrong_balances, $client) {
$client->invoices->where('is_deleted', false)->whereIn('status_id', '!=', Invoice::STATUS_DRAFT)->each(function ($invoice) use ($client) {
$total_amount = $invoice->payments()->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED])->get()->sum('pivot.amount');
$total_refund = $invoice->payments()->get()->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED])->sum('pivot.refunded');
$total_credit = $invoice->credits()->get()->sum('amount');
@ -357,7 +360,7 @@ class CheckData extends Command
$calculated_paid_amount = $invoice->amount - $invoice->balance - $total_credit;
if ((string)$total_paid != (string)($invoice->amount - $invoice->balance - $total_credit)) {
$wrong_balances++;
$this->wrong_balances++;
$this->logMessage($client->present()->name.' - '.$client->id." - Total Amount = {$total_amount} != Calculated Total = {$calculated_paid_amount} - Total Refund = {$total_refund} Total credit = {$total_credit}");
@ -367,13 +370,13 @@ class CheckData extends Command
});
$this->logMessage("{$wrong_balances} clients with incorrect invoice balances");
$this->logMessage("{$this->wrong_balances} clients with incorrect invoice balances");
}
private function checkClientBalances()
{
$wrong_balances = 0;
$wrong_paid_to_dates = 0;
$this->wrong_balances = 0;
$this->wrong_paid_to_dates = 0;
foreach (Client::cursor()->where('is_deleted', 0) as $client) {
//$invoice_balance = $client->invoices->where('is_deleted', false)->where('status_id', '>', 1)->sum('balance');
@ -387,14 +390,14 @@ class CheckData extends Command
$ledger = CompanyLedger::where('client_id', $client->id)->orderBy('id', 'DESC')->first();
if ($ledger && (string) $invoice_balance != (string) $client->balance) {
$wrong_paid_to_dates++;
$this->wrong_paid_to_dates++;
$this->logMessage($client->present()->name.' - '.$client->id." - calculated client balances do not match Invoice Balances = {$invoice_balance} - Client Balance = ".rtrim($client->balance, '0'). " Ledger balance = {$ledger->balance}");
$this->isValid = false;
}
}
$this->logMessage("{$wrong_paid_to_dates} clients with incorrect client balances");
$this->logMessage("{$this->wrong_paid_to_dates} clients with incorrect client balances");
}
//fix for client balances =
@ -406,8 +409,8 @@ class CheckData extends Command
private function checkInvoiceBalances()
{
$wrong_balances = 0;
$wrong_paid_to_dates = 0;
$this->wrong_balances = 0;
$this->wrong_paid_to_dates = 0;
foreach (Client::where('is_deleted', 0)->cursor() as $client) {
$invoice_balance = $client->invoices()->where('is_deleted', false)->where('status_id', '>', 1)->get()->sum('balance');
@ -419,14 +422,14 @@ class CheckData extends Command
$ledger = CompanyLedger::where('client_id', $client->id)->orderBy('id', 'DESC')->first();
if ($ledger && number_format($invoice_balance, 4) != number_format($client->balance, 4)) {
$wrong_balances++;
$this->wrong_balances++;
$this->logMessage("# {$client->id} " . $client->present()->name.' - '.$client->number." - Balance Failure - Invoice Balances = {$invoice_balance} Client Balance = {$client->balance} Ledger Balance = {$ledger->balance}");
$this->isValid = false;
}
}
$this->logMessage("{$wrong_balances} clients with incorrect balances");
$this->logMessage("{$this->wrong_balances} clients with incorrect balances");
}
private function checkLogoFiles()

View File

@ -27,4 +27,7 @@ class PaymentMethodMeta
/** @var int */
public $type;
/** @var string */
public $state;
}

View File

@ -179,8 +179,6 @@ class LoginController extends BaseController
$user = $this->guard()->user();
event(new UserLoggedIn($user, $user->account->default_company, Ninja::eventVars($user->id)));
//2FA
if($user->google_2fa_secret && $request->has('one_time_password'))
{
@ -226,6 +224,8 @@ class LoginController extends BaseController
if(Ninja::isHosted() && !$cu->first()->is_owner && !$user->account->isEnterpriseClient())
return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
event(new UserLoggedIn($user, $user->account->default_company, Ninja::eventVars($user->id)));
return $this->timeConstrainedResponse($cu);

View File

@ -379,8 +379,6 @@ class BaseController extends Controller
'company.designs'=> function ($query) use ($created_at, $user) {
$query->where('created_at', '>=', $created_at)->with('company');
if(!$user->isAdmin())
$query->where('designs.user_id', $user->id);
},
'company.documents'=> function ($query) use ($created_at, $user) {
$query->where('created_at', '>=', $created_at);
@ -388,22 +386,14 @@ class BaseController extends Controller
'company.groups' => function ($query) use ($created_at, $user) {
$query->where('created_at', '>=', $created_at);
if(!$user->isAdmin())
$query->where('group_settings.user_id', $user->id);
},
'company.payment_terms'=> function ($query) use ($created_at, $user) {
$query->where('created_at', '>=', $created_at);
if(!$user->isAdmin())
$query->where('payment_terms.user_id', $user->id);
},
'company.tax_rates' => function ($query) use ($created_at, $user) {
$query->where('created_at', '>=', $created_at);
if(!$user->isAdmin())
$query->where('tax_rates.user_id', $user->id);
},
'company.activities'=> function ($query) use($user) {
@ -519,8 +509,6 @@ class BaseController extends Controller
'company.payment_terms'=> function ($query) use ($created_at, $user) {
$query->where('created_at', '>=', $created_at);
if(!$user->isAdmin())
$query->where('payment_terms.user_id', $user->id);
},
'company.products' => function ($query) use ($created_at, $user) {
@ -561,9 +549,6 @@ class BaseController extends Controller
'company.tax_rates' => function ($query) use ($created_at, $user) {
$query->where('created_at', '>=', $created_at);
if(!$user->isAdmin())
$query->where('tax_rates.user_id', $user->id);
},
'company.vendors'=> function ($query) use ($created_at, $user) {
$query->where('created_at', '>=', $created_at)->with('contacts', 'documents');
@ -575,9 +560,6 @@ class BaseController extends Controller
'company.expense_categories'=> function ($query) use ($created_at, $user) {
$query->where('created_at', '>=', $created_at);
if(!$user->isAdmin())
$query->where('expense_categories.user_id', $user->id);
},
'company.task_statuses'=> function ($query) use ($created_at, $user) {
$query->where('created_at', '>=', $created_at);

View File

@ -168,7 +168,8 @@ class InvoiceController extends Controller
if ($invoices->count() == 1) {
$invoice = $invoices->first();
$invitation = $invoice->invitations->first();
$file = $invoice->pdf_file_path($invitation);
//$file = $invoice->pdf_file_path($invitation);
$file = $invoice->service()->getInvoicePdf(auth()->user());
return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);;
}

View File

@ -51,7 +51,8 @@ class PaymentMethodController extends Controller
$gateway = $this->getClientGateway();
$data['gateway'] = $gateway;
$data['client'] = auth()->user()->client;
return $gateway
->driver(auth()->user()->client)
->setPaymentMethod($request->query('method'))
@ -91,9 +92,9 @@ class PaymentMethodController extends Controller
public function verify(ClientGatewayToken $payment_method)
{
$gateway = $this->getClientGateway();
return $gateway
// $gateway = $this->getClientGateway();
return $payment_method->gateway
->driver(auth()->user()->client)
->setPaymentMethod(request()->query('method'))
->verificationView($payment_method);
@ -101,9 +102,9 @@ class PaymentMethodController extends Controller
public function processVerification(Request $request, ClientGatewaytoken $payment_method)
{
$gateway = $this->getClientGateway();
// $gateway = $this->getClientGateway();
return $gateway
return $payment_method->gateway
->driver(auth()->user()->client)
->setPaymentMethod(request()->query('method'))
->processVerification($request, $payment_method);
@ -117,9 +118,9 @@ class PaymentMethodController extends Controller
*/
public function destroy(ClientGatewayToken $payment_method)
{
$gateway = $this->getClientGateway();
// $gateway = $this->getClientGateway();
$gateway
$payment_method->gateway
->driver(auth()->user()->client)
->setPaymentMethod(request()->query('method'))
->detach($payment_method);

View File

@ -7,7 +7,7 @@
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Http\Controllers\ClientPortal;

View File

@ -0,0 +1,55 @@
<?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://www.elastic.co/licensing/elastic-license
*/
namespace App\Http\Controllers;
use App\Libraries\MultiDB;
use App\Models\Company;
use App\Models\CompanyGateway;
use App\Models\User;
use App\PaymentDrivers\WePayPaymentDriver;
use App\Utils\Traits\MakesHash;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
class WePayController extends BaseController
{
use MakesHash;
/**
* Initialize WePay Signup.
*/
public function signup(string $token)
{
$hash = Cache::get($token);
MultiDB::findAndSetDbByCompanyKey($hash['company_key']);
$user = User::findOrFail($hash['user_id']);
$company = Company::where('company_key', $hash['company_key'])->firstOrFail();
$data['user_id'] = $user->id;
$data['company'] = $company;
$wepay_driver = new WePayPaymentDriver(new CompanyGateway, null, null);
return $wepay_driver->setup($data);
}
public function finished()
{
return render('gateways.wepay.signup.finished');
}
}

View File

@ -0,0 +1,200 @@
<?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://www.elastic.co/licensing/elastic-license
*/
namespace App\Http\Livewire;
use App\DataMapper\FeesAndLimits;
use App\Factory\CompanyGatewayFactory;
use App\Libraries\MultiDB;
use App\Models\Company;
use App\Models\CompanyGateway;
use App\Models\GatewayType;
use App\Models\User;
use App\PaymentDrivers\WePayPaymentDriver;
use Illuminate\Support\Facades\Hash;
use Livewire\Component;
use WePay;
class WepaySignup extends Component
{
public $user;
public $user_id;
public $company_key;
public $first_name;
public $last_name;
public $email;
public $company_name;
public $country;
public $ach;
public $wepay_payment_tos_agree;
public $debit_cards;
public $terms;
public $privacy_policy;
public $saved;
public $company;
protected $rules = [
'first_name' => ['required'],
'last_name' => ['required'],
'email' => ['required', 'email'],
'company_name' => ['required'],
'country' => ['required'],
'ach' => ['sometimes'],
'wepay_payment_tos_agree' => ['accepted'],
'debit_cards' => ['sometimes'],
];
public function mount()
{
MultiDB::setDb($this->company->db);
$user = User::find($this->user_id);
$this->company = Company::where('company_key', $this->company->company_key)->first();
$this->fill([
'wepay_payment_tos_agree' => '',
'ach' => '',
'country' => 'US',
'user' => $user,
'first_name' => $user->first_name,
'last_name' => $user->last_name,
'email' => $user->email,
'company_name' => $this->company->present()->name(),
'saved' => ctrans('texts.confirm'),
'terms' => '<a href="https://go.wepay.com/terms-of-service" target="_blank">'.ctrans('texts.terms_of_service').'</a>',
'privacy_policy' => '<a href="https://go.wepay.com/privacy-policy" target="_blank">'.ctrans('texts.privacy_policy').'</a>',
]);
}
public function render()
{
return render('gateways.wepay.signup.wepay-signup');
}
public function submit()
{
$data = $this->validate($this->rules);
//need to create or get a new WePay CompanyGateway
$cg = CompanyGateway::where('gateway_key', '8fdeed552015b3c7b44ed6c8ebd9e992')
->where('company_id', $this->company->id)
->firstOrNew();
if(!$cg->id) {
$fees_and_limits = new \stdClass;
$fees_and_limits->{GatewayType::CREDIT_CARD} = new FeesAndLimits;
$fees_and_limits->{GatewayType::BANK_TRANSFER} = new FeesAndLimits;
$cg = CompanyGatewayFactory::create($this->company->id, $this->user->id);
$cg->gateway_key = '8fdeed552015b3c7b44ed6c8ebd9e992';
$cg->require_cvv = false;
$cg->require_billing_address = false;
$cg->require_shipping_address = false;
$cg->update_details = false;
$cg->config = encrypt(config('ninja.testvars.checkout'));
$cg->fees_and_limits = $fees_and_limits;
$cg->save();
}
$this->saved = ctrans('texts.processing');
$wepay_driver = new WePayPaymentDriver($cg, null, null);
$wepay = $wepay_driver->init()->wepay;
$user_details = [
'client_id' => config('ninja.wepay.client_id'),
'client_secret' => config('ninja.wepay.client_secret'),
'email' => $data['email'],
'first_name' => $data['first_name'],
'last_name' => $data['last_name'],
'original_ip' => request()->ip(),
'original_device' => request()->server('HTTP_USER_AGENT'),
'tos_acceptance_time' => time(),
'redirect_uri' => route('wepay.finished'),
'scope' => 'manage_accounts,collect_payments,view_user,preapprove_payments,send_money',
];
$wepay_user = $wepay->request('user/register/', $user_details);
$access_token = $wepay_user->access_token;
$access_token_expires = $wepay_user->expires_in ? (time() + $wepay_user->expires_in) : null;
$wepay = new WePay($access_token);
$account_details = [
'name' => $data['company_name'],
'description' => ctrans('texts.wepay_account_description'),
'theme_object' => json_decode('{"name":"Invoice Ninja","primary_color":"0b4d78","secondary_color":"0b4d78","background_color":"f8f8f8","button_color":"33b753"}'),
'callback_uri' => route('payment_webhook', ['company_key' => $this->company->company_key, 'company_gateway_id' => $cg->hashed_id]),
'rbits' => $this->company->rBits(),
'country' => $data['country'],
];
if ($data['country'] == 'CA') {
$account_details['currencies'] = ['CAD'];
$account_details['country_options'] = ['debit_opt_in' => boolval($data['debit_cards'])];
} elseif ($data['country'] == 'GB') {
$account_details['currencies'] = ['GBP'];
}
$wepay_account = $wepay->request('account/create/', $account_details);
try {
$wepay->request('user/send_confirmation/', []);
$confirmation_required = true;
} catch (\WePayException $ex) {
if ($ex->getMessage() == 'This access_token is already approved.') {
$confirmation_required = false;
} else {
request()->session()->flash('message', $ex->getMessage());
}
nlog("failed in try catch ");
nlog($ex->getMessage());
}
$config = [
'userId' => $wepay_user->user_id,
'accessToken' => $access_token,
'tokenType' => $wepay_user->token_type,
'tokenExpires' => $access_token_expires,
'accountId' => $wepay_account->account_id,
'state' => $wepay_account->state,
'testMode' => config('ninja.wepay.environment') == 'staging',
'country' => $data['country'],
];
$cg->setConfig($config);
$cg->save();
if ($confirmation_required) {
request()->session()->flash('message', trans('texts.created_wepay_confirmation_required'));
} else {
$update_uri = $wepay->request('/account/get_update_uri', [
'account_id' => $wepay_account->account_id,
'redirect_uri' => config('ninja.app_url'),
]);
return redirect($update_uri->uri);
}
return redirect()->to('/wepay/finished');
}
}

View File

@ -7,7 +7,7 @@
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Http\Requests\ClientPortal\Credits;

View File

@ -7,7 +7,7 @@
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Http\Requests\ClientPortal\Invoices;

View File

@ -7,7 +7,7 @@
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Http\Requests\ClientPortal\Invoices;

View File

@ -7,7 +7,7 @@
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Http\Requests\ClientPortal\Quotes;

View File

@ -7,7 +7,7 @@
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Http\Requests\ClientPortal\Quotes;

View File

@ -7,7 +7,7 @@
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Http\Requests\ClientPortal\Quotes;

View File

@ -7,7 +7,7 @@
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Http\Requests\ClientPortal\RecurringInvoices;

View File

@ -7,7 +7,7 @@
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Http\Requests\ClientPortal\Tasks;

View File

@ -484,7 +484,7 @@ class CompanyExport implements ShouldQueue
if(!Storage::disk(config('filesystems.default'))->exists($path))
Storage::disk(config('filesystems.default'))->makeDirectory($path, 0775);
$zip_path = public_path('storage/backups/'.$file_name);
$zip = new \ZipArchive();

View File

@ -214,12 +214,12 @@ class Import implements ShouldQueue
// if(Ninja::isHosted() && array_key_exists('ninja_tokens', $data))
$this->processNinjaTokens($data['ninja_tokens']);
$this->fixData();
$this->setInitialCompanyLedgerBalances();
// $this->fixClientBalances();
$check_data = CheckCompanyData::dispatchNow($this->company, md5(time()));
try{
Mail::to($this->user->email, $this->user->name())
@ -247,6 +247,41 @@ class Import implements ShouldQueue
unlink($this->file_path);
}
private function fixData()
{
$this->company->clients()->withTrashed()->where('is_deleted', 0)->cursor()->each(function ($client) {
$total_invoice_payments = 0;
$credit_total_applied = 0;
foreach ($client->invoices()->where('is_deleted', false)->where('status_id', '>', 1)->get() as $invoice) {
$total_amount = $invoice->payments()->where('is_deleted', false)->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED, Payment::STATUS_REFUNDED])->get()->sum('pivot.amount');
$total_refund = $invoice->payments()->where('is_deleted', false)->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED, Payment::STATUS_REFUNDED])->get()->sum('pivot.refunded');
$total_invoice_payments += ($total_amount - $total_refund);
}
// 10/02/21
foreach ($client->payments as $payment) {
$credit_total_applied += $payment->paymentables()->where('paymentable_type', App\Models\Credit::class)->get()->sum(\DB::raw('amount'));
}
if ($credit_total_applied < 0) {
$total_invoice_payments += $credit_total_applied;
}
if (round($total_invoice_payments, 2) != round($client->paid_to_date, 2)) {
$client->paid_to_date = $total_invoice_payments;
$client->save();
}
});
}
private function setInitialCompanyLedgerBalances()
{
Client::cursor()->each(function ($client) {
@ -1175,10 +1210,14 @@ class Import implements ShouldQueue
if($try_quote && array_key_exists('quotes', $this->ids) ) {
$quote_id = $this->transformId('quotes', $resource['invoice_id']);
$entity = Quote::where('id', $quote_id)->withTrashed()->first();
$exception = $e;
try{
$quote_id = $this->transformId('quotes', $resource['invoice_id']);
$entity = Quote::where('id', $quote_id)->withTrashed()->first();
}
catch(\Exception $e){
nlog("i couldn't find the quote document {$resource['invoice_id']}, perhaps it is a quote?");
nlog($e->getMessage());
}
}
if(!$entity)
@ -1642,7 +1681,6 @@ class Import implements ShouldQueue
public function exec($method, $url, $data)
{
nlog($this->token);
$client = new \GuzzleHttp\Client(['headers' =>
[

View File

@ -37,7 +37,8 @@ class MigrationCompleted extends Mailable
$data['company'] = $this->company->fresh();
$data['whitelabel'] = $this->company->account->isPaid() ? true : false;
$data['check_data'] = $this->check_data;
$data['logo'] = $this->company->present()->logo();
$result = $this->from(config('mail.from.address'), config('mail.from.name'))
->view('email.import.completed', $data);

View File

@ -52,12 +52,15 @@ class SupportMessageSent extends Mailable
$account = auth()->user()->account;
$plan = $account->plan ?: 'Self Hosted';
$plan = $account->plan ?: 'Free Self Hosted';
$company = auth()->user()->company();
$user = auth()->user();
$subject = "Customer MSG {$user->present()->name} - [{$plan} - DB:{$company->db}]";
if(Ninja::isHosted())
$subject = "Hosted {$user->present()->name} - [{$plan} - DB:{$company->db}]";
else
$subject = "Self Host {$user->present()->name} - [{$plan} - DB:{$company->db}]";
return $this->from(config('mail.from.address'), config('mail.from.name'))
->replyTo($user->email, $user->present()->name())

View File

@ -468,4 +468,39 @@ class Company extends BaseModel
{
return $this->slack_webhook_url;
}
public function rBits()
{
$user = $this->owner();
$data = [];
$data[] = $this->createRBit('business_name', 'user', ['business_name' => $this->present()->name()]);
$data[] = $this->createRBit('industry_code', 'user', ['industry_detail' => $this->industry ? $this->industry->name : '']);
$data[] = $this->createRBit('comment', 'partner_database', ['comment_text' => 'Logo image not present']);
$data[] = $this->createRBit('business_description', 'user', ['business_description' => $this->present()->size()]);
$data[] = $this->createRBit('person', 'user', ['name' => $user->present()->getFullName()]);
$data[] = $this->createRBit('email', 'user', ['email' => $user->email]);
$data[] = $this->createRBit('phone', 'user', ['phone' => $user->phone]);
$data[] = $this->createRBit('website_uri', 'user', ['uri' => $this->settings->website]);
$data[] = $this->createRBit('external_account', 'partner_database', ['is_partner_account' => 'yes', 'account_type' => 'Invoice Ninja', 'create_time' => time()]);
return $data;
}
private function createRBit($type, $source, $properties)
{
$data = new \stdClass;
$data->receive_time = time();
$data->type = $type;
$data->source = $source;
$data->properties = new \stdClass;
foreach ($properties as $key => $val) {
$data->properties->$key = $val;
}
return $data;
}
}

View File

@ -95,6 +95,10 @@ class Gateway extends StaticModel
case 39:
return [GatewayType::CREDIT_CARD => ['refund' => true, 'token_billing' => true]]; //Checkout
break;
case 49:
return [GatewayType::CREDIT_CARD => ['refund' => true, 'token_billing' => true],
GatewayType::BANK_TRANSFER => ['refund' => true, 'token_billing' => true]]; //WePay
break;
case 50:
return [
GatewayType::CREDIT_CARD => ['refund' => true, 'token_billing' => true],

View File

@ -59,6 +59,8 @@ class Payment extends BaseModel
'date',
'transaction_reference',
'number',
'exchange_currency_id',
'exchange_rate',
// 'is_manual',
'private_notes',
'custom_value1',

View File

@ -106,4 +106,8 @@ class CompanyPresenter extends EntityPresenter
"SPC\n0200\n1\n{$user_iban}\nK\n{$this->name}\n{$settings->address1}\n{$settings->postal_code} {$settings->city}\n\n\nCH\n\n\n\n\n\n\n\n{$balance_due_raw}\n{$client_currency}\n\n\n\n\n\n\n\nNON\n\n{$invoice_number}\nEPD\n";
}
public function size()
{
return $this->entity->size ? $this->entity->size->name : '';
}
}

View File

@ -41,4 +41,35 @@ class InvoicePresenter extends EntityPresenter
return '';
}
}
public function rBits()
{
$properties = new \stdClass();
$properties->terms_text = $this->terms;
$properties->note = $this->public_notes;
$properties->itemized_receipt = [];
foreach ($this->line_items as $item) {
$properties->itemized_receipt[] = $this->itemRbits($item);
}
$data = new stdClass();
$data->receive_time = time();
$data->type = 'transaction_details';
$data->source = 'user';
$data->properties = $properties;
return [$data];
}
public function itemRbits($item)
{
$data = new stdClass();
$data->description = $item->notes;
$data->item_price = floatval($item->cost);
$data->quantity = floatval($item->quantity);
$data->amount = round($data->item_price * $data->quantity, 2);
return $data;
}
}

View File

@ -26,4 +26,28 @@ class UserPresenter extends EntityPresenter
return $first_name.' '.$last_name;
}
public function getDisplayName()
{
if ($this->getFullName()) {
return $this->getFullName();
} elseif ($this->entity->email) {
return $this->entity->email;
} else {
return ctrans('texts.guest');
}
}
/**
* @return string
*/
public function getFullName()
{
if ($this->entity->first_name || $this->entity->last_name) {
return $this->entity->first_name.' '.$this->entity->last_name;
} else {
return '';
}
}
}

View File

@ -66,6 +66,8 @@ class SystemLog extends Model
const TYPE_AUTHORIZE = 305;
const TYPE_CUSTOM = 306;
const TYPE_BRAINTREE = 307;
const TYPE_WEPAY = 309;
const TYPE_QUOTA_EXCEEDED = 400;
const TYPE_UPSTREAM_FAILURE = 401;

View File

@ -160,6 +160,17 @@ class BaseDriver extends AbstractPaymentDriver
{
}
/**
* Detaches a payment method from the gateway
*
* @param ClientGatewayToken $token The gateway token
* @return bool boolean response
*/
public function detach(ClientGatewayToken $token)
{
return true;
}
/**
* Set the inbound request payment method type for access.
*

View File

@ -167,7 +167,7 @@ class ACH
return $this->processUnsuccessfulPayment($state);
} catch (Exception $e) {
if ($e instanceof CardException) {
return redirect()->route('client.payment_methods.verification', ['id' => ClientGatewayToken::first()->hashed_id, 'method' => GatewayType::BANK_TRANSFER]);
return redirect()->route('client.payment_methods.verification', ['payment_method' => ClientGatewayToken::first()->hashed_id, 'method' => GatewayType::BANK_TRANSFER]);
}
throw new PaymentFailed($e->getMessage(), $e->getCode());

View File

@ -47,164 +47,5 @@ class Account
]);
}
/*** 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,265 @@
<?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://www.elastic.co/licensing/elastic-license
*/
namespace App\PaymentDrivers\WePay;
use App\Exceptions\PaymentFailed;
use App\Models\ClientGatewayToken;
use App\Models\GatewayType;
use App\Models\Payment;
use App\PaymentDrivers\WePayPaymentDriver;
use App\PaymentDrivers\WePay\WePayCommon;
use App\Utils\Traits\MakesHash;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
class ACH
{
use MakesHash;
use WePayCommon;
public $wepay_payment_driver;
public function __construct(WePayPaymentDriver $wepay_payment_driver)
{
$this->wepay_payment_driver = $wepay_payment_driver;
}
public function authorizeView($data)
{
$data['gateway'] = $this->wepay_payment_driver;
return render('gateways.wepay.authorize.bank_transfer', $data);
}
public function authorizeResponse($request)
{
//https://developer.wepay.com/api/api-calls/credit_card#authorize
$data = $request->all();
// authorize the credit card
nlog($data);
/*
'_token' => '1Fk5CRj34up5ntKPvrFyMIAJhDdUNF3boqT3iIN3',
'company_gateway_id' => '39',
'payment_method_id' => '1',
'gateway_response' => NULL,
'is_default' => NULL,
'credit_card_id' => '180642154638',
'q' => '/client/payment_methods',
'method' => '1',
*/
$response = $this->wepay_payment_driver->wepay->request('payment_bank/persist', [
'client_id' => config('ninja.wepay.client_id'),
'client_secret' => config('ninja.wepay.client_secret'),
'payment_bank_id' => (int)$data['bank_account_id'],
]);
// display the response
// nlog($response);
if(in_array($response->state, ['new', 'pending', 'authorized'])){
$this->storePaymentMethod($response, GatewayType::BANK_TRANSFER);
return redirect()->route('client.payment_methods.index');
}
throw new PaymentFailed("There was a problem adding this payment method.", 400);
/*
{
"payment_bank_id": 12345,
"bank_name": "Wells Fargo",
"account_last_four": "6789",
"state": "authorized"
}
state options: new, pending, authorized, disabled.
*/
}
/* If the bank transfer token is PENDING - we need to verify!! */
//
public function verificationView(ClientGatewayToken $token)
{
$this->wepay_payment_driver->init();
$data = [
'token' => $token,
'gateway' => $this->wepay_payment_driver,
];
return render('gateways.wepay.authorize.verify', $data);
}
/**
{
"client_id": 1234,
"client_secret": "b1fc2f68-4d1f-4a",
"payment_bank_id": 12345,
"type": "microdeposits",
"microdeposits": [
8,
12
]
}
*/
public function processVerification(Request $request, ClientGatewayToken $token)
{
$transactions = $request->input('transactions');
$transformed_transactions = [];
foreach($transactions as $transaction)
$transformed_transactions[] = (int)$transaction;
try {
$response = $this->wepay_payment_driver->wepay->request('payment_bank/verify', [
'client_id' => config('ninja.wepay.client_id'),
'client_secret' => config('ninja.wepay.client_secret'),
'payment_bank_id' => $token->token,
'type' => 'microdeposits',
'microdeposits' => $transformed_transactions,
]);
}
catch(\Exception $e){
return redirect()->route('client.payment_methods.verification', ['payment_method' => $token->hashed_id, 'method' => GatewayType::BANK_TRANSFER])
->with('error', $e->getMessage());
}
/*
{
"payment_bank_id": 12345,
"bank_name": "Wells Fargo",
"account_last_four": "6789",
"state": "authorized"
}
*/
nlog($response);
//$meta = $token->meta;
if($response->state == "authorized")
{
$meta = $token->meta;
$meta->state = $response->state;
$token->meta;
$token->save();
return redirect()->route('client.payment_methods.index');
}
else{
return redirect()->route('client.payment_methods.verification', ['payment_method' => $token->hashed_id, 'method' => GatewayType::BANK_TRANSFER])
->with('error', ctrans('texts.verification_failed'));
}
}
///////////////////////////////////////////////////////////////////////////////////////
public function paymentView(array $data)
{
$data['gateway'] = $this->wepay_payment_driver;
$data['currency'] = $this->wepay_payment_driver->client->getCurrencyCode();
$data['payment_method_id'] = GatewayType::BANK_TRANSFER;
$data['amount'] = $data['total']['amount_with_fee'];
return render('gateways.wepay.bank_transfer', $data);
}
public function paymentResponse($request)
{
nlog($request->all());
$token = ClientGatewayToken::find($this->decodePrimaryKey($request->input('source')));
$token_meta = $token->meta;
if($token_meta->state != "authorized")
return redirect()->route('client.payment_methods.verification', ['payment_method' => $token->hashed_id, 'method' => GatewayType::BANK_TRANSFER]);
$response = $this->wepay_payment_driver->wepay->request('checkout/create', array(
// 'callback_uri' => route('payment_webhook', ['company_key' => $this->wepay_payment_driver->company_gateway->company->company_key, 'company_gateway_id' => $this->wepay_payment_driver->company_gateway->hashed_id]),
'unique_id' => Str::random(40),
'account_id' => $this->wepay_payment_driver->company_gateway->getConfigField('accountId'),
'amount' => $this->wepay_payment_driver->payment_hash->data->amount_with_fee,
'currency' => $this->wepay_payment_driver->client->getCurrencyCode(),
'short_description' => 'A vacation home rental',
'type' => 'goods',
'payment_method' => array(
'type' => 'payment_bank',
'payment_bank' => array(
'id' => $token->token
)
)
));
/* Merge all data and store in the payment hash*/
$state = [
'server_response' => $response,
'payment_hash' => $request->payment_hash,
];
$state = array_merge($state, $request->all());
$this->wepay_payment_driver->payment_hash->data = array_merge((array) $this->wepay_payment_driver->payment_hash->data, $state);
$this->wepay_payment_driver->payment_hash->save();
if(in_array($response->state, ['authorized', 'captured'])){
//success
nlog("success");
$payment_status = $response->state == 'authorized' ? Payment::STATUS_COMPLETED : Payment::STATUS_PENDING;
return $this->processSuccessfulPayment($response, $payment_status, GatewayType::BANK_TRANSFER);
}
if(in_array($response->state, ['released', 'cancelled', 'failed', 'expired'])){
//some type of failure
nlog("failure");
$payment_status = $response->state == 'cancelled' ? Payment::STATUS_CANCELLED : Payment::STATUS_FAILED;
$this->processUnSuccessfulPayment($response, $payment_status);
}
}
private function storePaymentMethod($response, $payment_method_id)
{
$payment_meta = new \stdClass;
$payment_meta->exp_month = (string) '';
$payment_meta->exp_year = (string) '';
$payment_meta->brand = (string) $response->bank_name;
$payment_meta->last4 = (string) $response->account_last_four;
$payment_meta->type = GatewayType::BANK_TRANSFER;
$payment_meta->state = $response->state;
$data = [
'payment_meta' => $payment_meta,
'token' => $response->payment_bank_id,
'payment_method_id' => $payment_method_id,
];
$this->wepay_payment_driver->storeGatewayToken($data);
}
}

View File

@ -0,0 +1,281 @@
<?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://www.elastic.co/licensing/elastic-license
*/
namespace App\PaymentDrivers\WePay;
use App\Exceptions\PaymentFailed;
use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest;
use App\Jobs\Mail\PaymentFailureMailer;
use App\Jobs\Util\SystemLogger;
use App\Models\GatewayType;
use App\Models\Payment;
use App\Models\PaymentType;
use App\Models\SystemLog;
use App\PaymentDrivers\WePayPaymentDriver;
use App\PaymentDrivers\WePay\WePayCommon;
use Illuminate\Support\Str;
class CreditCard
{
use WePayCommon;
public $wepay_payment_driver;
public function __construct(WePayPaymentDriver $wepay_payment_driver)
{
$this->wepay_payment_driver = $wepay_payment_driver;
}
public function authorizeView($data)
{
$data['gateway'] = $this->wepay_payment_driver;
return render('gateways.wepay.authorize.authorize', $data);
}
public function authorizeResponse($request)
{
//https://developer.wepay.com/api/api-calls/credit_card#authorize
$data = $request->all();
// authorize the credit card
nlog($data);
/*
'_token' => '1Fk5CRj34up5ntKPvrFyMIAJhDdUNF3boqT3iIN3',
'company_gateway_id' => '39',
'payment_method_id' => '1',
'gateway_response' => NULL,
'is_default' => NULL,
'credit_card_id' => '180642154638',
'q' => '/client/payment_methods',
'method' => '1',
*/
$response = $this->wepay_payment_driver->wepay->request('credit_card/authorize', array(
'client_id' => config('ninja.wepay.client_id'),
'client_secret' => config('ninja.wepay.client_secret'),
'credit_card_id' => (int)$data['credit_card_id'],
));
// display the response
// nlog($response);
if(in_array($response->state, ['new', 'authorized'])){
$this->storePaymentMethod($response, GatewayType::CREDIT_CARD);
return redirect()->route('client.payment_methods.index');
}
throw new PaymentFailed("There was a problem adding this payment method.", 400);
/*
[credit_card_id] => 348084962473
[credit_card_name] => Visa xxxxxx4018
[state] => authorized
[user_name] => Joey Diaz
[email] => user@example.com
[create_time] => 1623798172
[expiration_month] => 10
[expiration_year] => 2023
[last_four] => 4018
[input_source] => card_keyed
[virtual_terminal_mode] => none
[card_on_file] =>
[recurring] =>
[cvv_provided] => 1
[auto_update] =>
*/
}
public function paymentView(array $data)
{
$data['gateway'] = $this->wepay_payment_driver;
$data['description'] = ctrans('texts.invoices') . ': ' . collect($data['invoices'])->pluck('invoice_number');
return render('gateways.wepay.credit_card.pay', $data);
}
public function paymentResponse(PaymentResponseRequest $request)
{
nlog("payment response");
//it could be an existing token or a new credit_card_id that needs to be converted into a wepay token
if($request->has('credit_card_id') && $request->input('credit_card_id'))
{
nlog("authorize the card first!");
$response = $this->wepay_payment_driver->wepay->request('credit_card/authorize', array(
// 'callback_uri' => route('payment_webhook', ['company_key' => $this->wepay_payment_driver->company_gateway->company->company_key, 'company_gateway_id' => $this->wepay_payment_driver->company_gateway->hashed_id]),
'client_id' => config('ninja.wepay.client_id'),
'client_secret' => config('ninja.wepay.client_secret'),
'credit_card_id' => (int)$request->input('credit_card_id'),
));
$credit_card_id = (int)$response->credit_card_id;
if(in_array($response->state, ['new', 'authorized']) && boolval($request->input('store_card'))){
$this->storePaymentMethod($response, GatewayType::CREDIT_CARD);
}
}
else {
$credit_card_id = (int)$request->input('token');
}
// USD, CAD, and GBP.
nlog($request->all());
// charge the credit card
$response = $this->wepay_payment_driver->wepay->request('checkout/create', array(
// 'callback_uri' => route('payment_webhook', ['company_key' => $this->wepay_payment_driver->company_gateway->company->company_key, 'company_gateway_id' => $this->wepay_payment_driver->company_gateway->hashed_id]),
'unique_id' => Str::random(40),
'account_id' => $this->wepay_payment_driver->company_gateway->getConfigField('accountId'),
'amount' => $this->wepay_payment_driver->payment_hash->data->amount_with_fee,
'currency' => $this->wepay_payment_driver->client->getCurrencyCode(),
'short_description' => 'A vacation home rental',
'type' => 'goods',
'payment_method' => array(
'type' => 'credit_card',
'credit_card' => array(
'id' => $credit_card_id
)
)
));
/* Merge all data and store in the payment hash*/
$state = [
'server_response' => $response,
'payment_hash' => $request->payment_hash,
];
$state = array_merge($state, $request->all());
$this->wepay_payment_driver->payment_hash->data = array_merge((array) $this->wepay_payment_driver->payment_hash->data, $state);
$this->wepay_payment_driver->payment_hash->save();
if(in_array($response->state, ['authorized', 'captured'])){
//success
nlog("success");
$payment_status = $response->state == 'authorized' ? Payment::STATUS_COMPLETED : Payment::STATUS_PENDING;
return $this->processSuccessfulPayment($response, $payment_status, GatewayType::CREDIT_CARD);
}
if(in_array($response->state, ['released', 'cancelled', 'failed', 'expired'])){
//some type of failure
nlog("failure");
$payment_status = $response->state == 'cancelled' ? Payment::STATUS_CANCELLED : Payment::STATUS_FAILED;
$this->processUnSuccessfulPayment($response, $payment_status);
}
}
/*
new The checkout was created by the application. This state typically indicates that checkouts created in WePay's hosted checkout flow are waiting for the payer to submit their information.
authorized The payer entered their payment info and confirmed the payment on WePay. WePay has successfully charged the card.
captured The payment has been reserved from the payer.
released The payment has been credited to the payee account. Note that the released state may be active although there are active partial refunds or partial chargebacks.
cancelled The payment has been cancelled by the payer, payee, or application.
refunded The payment was captured and then refunded by the payer, payee, or application. The payment has been debited from the payee account.
charged back The payment has been charged back by the payer and the payment has been debited from the payee account.
failed The payment has failed.
expired Checkouts expire if they remain in the new state for more than 30 minutes (e.g., they have been abandoned).
*/
/*
https://developer.wepay.com/api/api-calls/checkout
{
"checkout_id": 649945633,
"account_id": 1548718026,
"type": "donation",
"short_description": "test checkout",
"currency": "USD",
"amount": 20,
"state": "authorized",
"soft_descriptor": "WPY*Wolverine",
"auto_release": true,
"create_time": 1463589958,
"gross": 20.88,
"reference_id": null,
"callback_uri": null,
"long_description": null,
"delivery_type": null,
"initiated_by": "merchant",
"in_review": false,
"fee": {
"app_fee": 0,
"processing_fee": 0.88,
"fee_payer": "payer"
},
"chargeback": {
"amount_charged_back": 0,
"dispute_uri": null
},
"refund": {
"amount_refunded": 0,
"refund_reason": null
},
"payment_method": {
"type": "credit_card",
"credit_card": {
"id": 1684847614,
"data": {
"emv_receipt": null,
"signature_url": null
},
"auto_release": false
}
},
"hosted_checkout": null,
"payer": {
"email": "test@example.com",
"name": "Mr Smith",
"home_address": null
},
"npo_information": null,
"payment_error": null
}
*/
private function storePaymentMethod($response, $payment_method_id)
{
nlog("storing card");
$payment_meta = new \stdClass;
$payment_meta->exp_month = (string) $response->expiration_month;
$payment_meta->exp_year = (string) $response->expiration_year;
$payment_meta->brand = (string) $response->credit_card_name;
$payment_meta->last4 = (string) $response->last_four;
$payment_meta->type = GatewayType::CREDIT_CARD;
$data = [
'payment_meta' => $payment_meta,
'token' => $response->credit_card_id,
'payment_method_id' => $payment_method_id,
];
$this->wepay_payment_driver->storeGatewayToken($data);
}
}

View File

@ -0,0 +1,37 @@
<?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://www.elastic.co/licensing/elastic-license
*/
namespace App\PaymentDrivers\WePay;
use App\PaymentDrivers\WePayPaymentDriver;
use Illuminate\Http\Request;
class Setup
{
public $wepay;
public function __construct(WePayPaymentDriver $wepay)
{
$this->wepay = $wepay;
}
public function boot($data)
{
/*
'user_id',
'company',
*/
return render('gateways.wepay.signup.index', $data);
}
}

View File

@ -0,0 +1,76 @@
<?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://www.elastic.co/licensing/elastic-license
*/
namespace App\PaymentDrivers\WePay;
use App\Exceptions\PaymentFailed;
use App\Jobs\Mail\PaymentFailureMailer;
use App\Jobs\Util\SystemLogger;
use App\Models\PaymentType;
use App\Models\SystemLog;
trait WePayCommon
{
private function processSuccessfulPayment($response, $payment_status, $gateway_type)
{
$data = [
'payment_type' => PaymentType::CREDIT_CARD_OTHER,
'amount' => $response->amount,
'transaction_reference' => $response->checkout_id,
'gateway_type_id' => $gateway_type,
];
$payment = $this->wepay_payment_driver->createPayment($data, $payment_status);
SystemLogger::dispatch(
['response' => $this->wepay_payment_driver->payment_hash->data->server_response, 'data' => $data],
SystemLog::CATEGORY_GATEWAY_RESPONSE,
SystemLog::EVENT_GATEWAY_SUCCESS,
SystemLog::TYPE_WEPAY,
$this->wepay_payment_driver->client,
$this->wepay_payment_driver->client->company,
);
return redirect()->route('client.payments.show', ['payment' => $this->wepay_payment_driver->encodePrimaryKey($payment->id)]);
}
private function processUnSuccessfulPayment($response, $payment_status)
{
PaymentFailureMailer::dispatch($this->wepay_payment_driver->client, $response->state, $this->wepay_payment_driver->client->company, $response->amount);
PaymentFailureMailer::dispatch(
$this->wepay_payment_driver->client,
$response,
$this->wepay_payment_driver->client->company,
$response->gross
);
$message = [
'server_response' => $response,
'data' => $this->wepay_payment_driver->payment_hash->data,
];
SystemLogger::dispatch(
$message,
SystemLog::CATEGORY_GATEWAY_RESPONSE,
SystemLog::EVENT_GATEWAY_FAILURE,
SystemLog::TYPE_WEPAY,
$this->wepay_payment_driver->client,
$this->wepay_payment_driver->client->company,
);
throw new PaymentFailed('Failed to process the payment.', 500);
}
}

View File

@ -0,0 +1,357 @@
<?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://www.elastic.co/licensing/elastic-license
*/
namespace App\PaymentDrivers;
use App\Http\Requests\Payments\PaymentWebhookRequest;
use App\Models\ClientGatewayToken;
use App\Models\GatewayType;
use App\Models\Payment;
use App\Models\PaymentHash;
use App\Models\SystemLog;
use App\PaymentDrivers\WePay\ACH;
use App\PaymentDrivers\WePay\CreditCard;
use App\PaymentDrivers\WePay\Setup;
use App\Utils\Traits\MakesHash;
use Illuminate\Http\Request;
use WePay;
class WePayPaymentDriver extends BaseDriver
{
use MakesHash;
/* Does this gateway support refunds? */
public $refundable = true;
/* Does this gateway support token billing? */
public $token_billing = true;
/* Does this gateway support authorizations? */
public $can_authorise_credit_card = true;
/* Initialized gateway */
public $wepay;
/* Initialized payment method */
public $payment_method;
/* Maps the Payment Gateway Type - to its implementation */
public static $methods = [
GatewayType::CREDIT_CARD => CreditCard::class,
GatewayType::BANK_TRANSFER => ACH::class,
];
const SYSTEM_LOG_TYPE = SystemLog::TYPE_WEPAY;
public function init()
{
if (WePay::getEnvironment() == 'none') {
if(config('ninja.wepay.environment') == 'staging')
WePay::useStaging(config('ninja.wepay.client_id'), config('ninja.wepay.client_secret'));
else
WePay::useProduction(config('ninja.wepay.client_id'), config('ninja.wepay.client_secret'));
}
if ($this->company_gateway)
$this->wepay = new WePay($this->company_gateway->getConfigField('accessToken'));
else
$this->wepay = new WePay(null);
return $this;
}
/**
* Return the gateway types that have been enabled
*
* @return array
*/
public function gatewayTypes(): array
{
$types = [];
if($this->company_gateway->fees_and_limits->{GatewayType::BANK_TRANSFER}->is_enabled)
$types[] = GatewayType::CREDIT_CARD;
if($this->company_gateway->fees_and_limits->{GatewayType::BANK_TRANSFER}->is_enabled)
$types[] = GatewayType::BANK_TRANSFER;
return $types;
}
/**
* Setup the gateway
*
* @param array $data user_id + company
* @return view
*/
public function setup(array $data)
{
return (new Setup($this))->boot($data);
}
/**
* Set the payment method
*
* @param int $payment_method_id Alias of GatewayType
*/
public function setPaymentMethod($payment_method_id)
{
$class = self::$methods[$payment_method_id];
$this->payment_method = new $class($this);
return $this;
}
public function authorizeView(array $data)
{
$this->init();
$data['gateway'] = $this->wepay;
$client = $data['client'];
$contact = $client->primary_contact()->first() ? $client->primary_contact()->first() : $lient->contacts->first();
$data['contact'] = $contact;
return $this->payment_method->authorizeView($data); //this is your custom implementation from here
}
public function authorizeResponse($request)
{
$this->init();
return $this->payment_method->authorizeResponse($request); //this is your custom implementation from here
}
public function verificationView(ClientGatewayToken $cgt)
{
$this->init();
return $this->payment_method->verificationView($cgt);
}
public function processVerification(Request $request, ClientGatewayToken $cgt)
{
$this->init();
return $this->payment_method->processVerification($request, $cgt);
}
public function processPaymentView(array $data)
{
$this->init();
return $this->payment_method->paymentView($data); //this is your custom implementation from here
}
public function processPaymentResponse($request)
{
$this->init();
return $this->payment_method->paymentResponse($request); //this is your custom implementation from here
}
public function tokenBilling(ClientGatewayToken $cgt, PaymentHash $payment_hash)
{
return $this->payment_method->yourTokenBillingImplmentation(); //this is your custom implementation from here
}
public function processWebhookRequest(PaymentWebhookRequest $request, Payment $payment = null)
{
$this->init();
$input = $request->all();
$accountId = $this->company_gateway->getConfigField('accountId');
foreach (array_keys($input) as $key) {
if ('_id' == substr($key, -3)) {
$objectType = substr($key, 0, -3);
$objectId = $input[$key];
break;
}
}
if (! isset($objectType)) {
throw new Exception('Could not find object id parameter');
}
if ($objectType == 'credit_card') {
$payment_method = ClientGatewayToken::where('token', $objectId)->first();
if (! $paymentMethod)
throw new \Exception('Unknown payment method');
$source = $this->wepay->request('credit_card', array(
'client_id' => config('ninja.wepay.client_id'),
'client_secret' => config('ninja.wepay.client_secret'),
'credit_card_id' => (int)$objectId,
));
if ($source->state == 'deleted') {
$payment_method->delete();
} else {
//$this->paymentService->convertPaymentMethodFromWePay($source, null, $paymentMethod)->save();
}
return 'Processed successfully';
} elseif ($objectType == 'account') {
if ($accountId != $objectId) {
throw new \Exception('Unknown account');
}
$wepayAccount = $this->wepay->request('account', array(
'account_id' => (int)$objectId,
));
if ($wepayAccount->state == 'deleted') {
$this->company_gateway->delete();
} else {
$config->state = $wepayAccount->state;
$this->company_gateway->setConfig($config);
$this->company_gateway->save();
}
return ['message' => 'Processed successfully'];
} elseif ($objectType == 'checkout') {
$payment = Payment::where('company_id', $this->company_gateway->company_id)
->where('transaction_reference', '=', $objectId)
->first();
if (! $payment) {
throw new Exception('Unknown payment');
}
if ($payment->is_deleted) {
throw new \Exception('Payment is deleted');
}
$checkout = $this->wepay->request('checkout', array(
'checkout_id' => intval($objectId),
));
if ($checkout->state == 'captured') {
$payment->status_id = Payment::STATUS_COMPLETED;
$payment->save();
} elseif ($checkout->state == 'cancelled') {
$payment->service()->deletePayment()->save();
} elseif ($checkout->state == 'failed') {
$payment->status_id = Payment::STATUS_FAILED;
$payment->save();
}
return 'Processed successfully';
} else {
return 'Ignoring event';
}
return true;
}
public function refund(Payment $payment, $amount, $return_client_response = false)
{
$this->init();
$response = $this->wepay->request('checkout/refund', array(
'checkout_id' => $payment->transaction_reference,
'refund_reason' => 'Refund',
'amount' => $amount
));
return [
'transaction_reference' => $response->checkout_id,
'transaction_response' => json_encode($response),
'success' => $response->state == 'refunded' ? true : false,
'description' => 'refund',
'code' => 0,
];
}
public function detach(ClientGatewayToken $token)
{
/*Bank accounts cannot be deleted - only CC*/
if($token->gateway_type_id == 2)
return true;
$this->init();
$response = $this->wepay->request('/credit_card/delete', [
'client_id' => config('ninja.wepay.client_id'),
'client_secret' => config('ninja.wepay.client_secret'),
'credit_card_id' => intval($token->token),
]);
if ($response->state == 'deleted') {
return true;
} else {
throw new \Exception(trans('texts.failed_remove_payment_method'));
}
}
public function getClientRequiredFields(): array
{
$fields = [
['name' => 'client_postal_code', 'label' => ctrans('texts.postal_code'), 'type' => 'text', 'validation' => 'required'],
['name' => 'contact_email', 'label' => ctrans('texts.email'), 'type' => 'text', 'validation' => 'required'],
];
if ($this->company_gateway->require_client_name) {
$fields[] = ['name' => 'client_name', 'label' => ctrans('texts.client_name'), 'type' => 'text', 'validation' => 'required'];
}
if ($this->company_gateway->require_client_phone) {
$fields[] = ['name' => 'client_phone', 'label' => ctrans('texts.client_phone'), 'type' => 'tel', 'validation' => 'required'];
}
if ($this->company_gateway->require_contact_name) {
$fields[] = ['name' => 'contact_first_name', 'label' => ctrans('texts.first_name'), 'type' => 'text', 'validation' => 'required'];
$fields[] = ['name' => 'contact_last_name', 'label' => ctrans('texts.last_name'), 'type' => 'text', 'validation' => 'required'];
}
if ($this->company_gateway->require_contact_email) {
$fields[] = ['name' => 'contact_email', 'label' => ctrans('texts.email'), 'type' => 'text', 'validation' => 'required,email:rfc'];
}
if ($this->company_gateway->require_billing_address) {
$fields[] = ['name' => 'client_address_line_1', 'label' => ctrans('texts.address1'), 'type' => 'text', 'validation' => 'required'];
// $fields[] = ['name' => 'client_address_line_2', 'label' => ctrans('texts.address2'), 'type' => 'text', 'validation' => 'nullable'];
$fields[] = ['name' => 'client_city', 'label' => ctrans('texts.city'), 'type' => 'text', 'validation' => 'required'];
$fields[] = ['name' => 'client_state', 'label' => ctrans('texts.state'), 'type' => 'text', 'validation' => 'required'];
$fields[] = ['name' => 'client_country_id', 'label' => ctrans('texts.country'), 'type' => 'text', 'validation' => 'required'];
}
if ($this->company_gateway->require_shipping_address) {
$fields[] = ['name' => 'client_shipping_address_line_1', 'label' => ctrans('texts.shipping_address1'), 'type' => 'text', 'validation' => 'required'];
// $fields[] = ['name' => 'client_shipping_address_line_2', 'label' => ctrans('texts.shipping_address2'), 'type' => 'text', 'validation' => 'sometimes'];
$fields[] = ['name' => 'client_shipping_city', 'label' => ctrans('texts.shipping_city'), 'type' => 'text', 'validation' => 'required'];
$fields[] = ['name' => 'client_shipping_state', 'label' => ctrans('texts.shipping_state'), 'type' => 'text', 'validation' => 'required'];
$fields[] = ['name' => 'client_shipping_postal_code', 'label' => ctrans('texts.shipping_postal_code'), 'type' => 'text', 'validation' => 'required'];
$fields[] = ['name' => 'client_shipping_country_id', 'label' => ctrans('texts.shipping_country'), 'type' => 'text', 'validation' => 'required'];
}
return $fields;
}
}

View File

@ -330,8 +330,6 @@ class BaseRepository
$model = $model->calc()->getCredit();
// $model->ledger()->updateCreditBalance(-1*($state['finished_amount'] - $state['starting_amount']));
if (! $model->design_id)
$model->design_id = $this->decodePrimaryKey($client->getSetting('credit_design_id'));
@ -339,12 +337,18 @@ class BaseRepository
if ($model instanceof Quote) {
if (! $model->design_id)
$model->design_id = $this->decodePrimaryKey($client->getSetting('quote_design_id'));
$model = $model->calc()->getQuote();
}
if ($model instanceof RecurringInvoice) {
if (! $model->design_id)
$model->design_id = $this->decodePrimaryKey($client->getSetting('invoice_design_id'));
$model = $model->calc()->getRecurringInvoice();
}

View File

@ -90,6 +90,7 @@ class PaymentRepository extends BaseRepository {
$client->service()->updatePaidToDate($_credit_totals)->save();
}
}
}
/*Fill the payment*/
@ -184,6 +185,10 @@ class PaymentRepository extends BaseRepository {
*/
private function processExchangeRates($data, $payment)
{
if(array_key_exists('exchange_rate', $data) && isset($data['exchange_rate']))
return $payment;
$client = Client::find($data['client_id']);
$client_currency = $client->getSetting('currency_id');

View File

@ -70,27 +70,27 @@ class Statics
$data = [];
foreach (config('ninja.cached_tables') as $name => $class) {
if (!Cache::has($name)) {
// if (!Cache::has($name)) {
// // check that the table exists in case the migration is pending
// if (!Schema::hasTable((new $class())->getTable())) {
// continue;
// }
// if ($name == 'payment_terms') {
// $orderBy = 'num_days';
// } elseif ($name == 'fonts') {
// $orderBy = 'sort_order';
// } elseif (in_array($name, ['currencies', 'industries', 'languages', 'countries', 'banks'])) {
// $orderBy = 'name';
// } else {
// $orderBy = 'id';
// }
// $tableData = $class::orderBy($orderBy)->get();
// if ($tableData->count()) {
// Cache::forever($name, $tableData);
// }
// }
// check that the table exists in case the migration is pending
if (!Schema::hasTable((new $class())->getTable())) {
continue;
}
if ($name == 'payment_terms') {
$orderBy = 'num_days';
} elseif ($name == 'fonts') {
$orderBy = 'sort_order';
} elseif (in_array($name, ['currencies', 'industries', 'languages', 'countries', 'banks'])) {
$orderBy = 'name';
} else {
$orderBy = 'id';
}
$tableData = $class::orderBy($orderBy)->get();
if ($tableData->count()) {
Cache::forever($name, $tableData);
}
}
$data[$name] = Cache::get($name);
}

View File

@ -72,6 +72,7 @@
"turbo124/beacon": "^1.0",
"turbo124/laravel-gmail": "^5",
"webpatser/laravel-countries": "dev-master#75992ad",
"wepay/php-sdk": "^0.3",
"wildbit/swiftmailer-postmark": "^3.3"
},
"require-dev": {

369
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "551d077c3d25c2a962f0c2c270618582",
"content-hash": "013b0357f14c1782315168bc42234b34",
"packages": [
{
"name": "asm/php-ansible",
@ -159,16 +159,16 @@
},
{
"name": "aws/aws-sdk-php",
"version": "3.183.10",
"version": "3.184.2",
"source": {
"type": "git",
"url": "https://github.com/aws/aws-sdk-php.git",
"reference": "3995354f4791f8ca85f5208325cef9065e471f3b"
"reference": "78fe691ab466fecf195209672f6c00c5d4ed219a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/3995354f4791f8ca85f5208325cef9065e471f3b",
"reference": "3995354f4791f8ca85f5208325cef9065e471f3b",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/78fe691ab466fecf195209672f6c00c5d4ed219a",
"reference": "78fe691ab466fecf195209672f6c00c5d4ed219a",
"shasum": ""
},
"require": {
@ -243,9 +243,9 @@
"support": {
"forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
"issues": "https://github.com/aws/aws-sdk-php/issues",
"source": "https://github.com/aws/aws-sdk-php/tree/3.183.10"
"source": "https://github.com/aws/aws-sdk-php/tree/3.184.2"
},
"time": "2021-06-01T18:13:35+00:00"
"time": "2021-06-11T18:20:15+00:00"
},
{
"name": "bacon/bacon-qr-code",
@ -460,16 +460,16 @@
},
{
"name": "checkout/checkout-sdk-php",
"version": "1.0.15",
"version": "1.0.16",
"source": {
"type": "git",
"url": "https://github.com/checkout/checkout-sdk-php.git",
"reference": "f73f6478c966968f2f0b9a9525c4bfb9b8e046f1"
"reference": "81c6aa884fb586b8a9456aecf83b639fed205f86"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/checkout/checkout-sdk-php/zipball/f73f6478c966968f2f0b9a9525c4bfb9b8e046f1",
"reference": "f73f6478c966968f2f0b9a9525c4bfb9b8e046f1",
"url": "https://api.github.com/repos/checkout/checkout-sdk-php/zipball/81c6aa884fb586b8a9456aecf83b639fed205f86",
"reference": "81c6aa884fb586b8a9456aecf83b639fed205f86",
"shasum": ""
},
"require": {
@ -513,9 +513,9 @@
],
"support": {
"issues": "https://github.com/checkout/checkout-sdk-php/issues",
"source": "https://github.com/checkout/checkout-sdk-php/tree/1.0.15"
"source": "https://github.com/checkout/checkout-sdk-php/tree/1.0.16"
},
"time": "2021-05-25T07:59:04+00:00"
"time": "2021-06-03T13:52:46+00:00"
},
{
"name": "cleverit/ubl_invoice",
@ -804,16 +804,16 @@
},
{
"name": "composer/ca-bundle",
"version": "1.2.9",
"version": "1.2.10",
"source": {
"type": "git",
"url": "https://github.com/composer/ca-bundle.git",
"reference": "78a0e288fdcebf92aa2318a8d3656168da6ac1a5"
"reference": "9fdb22c2e97a614657716178093cd1da90a64aa8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/ca-bundle/zipball/78a0e288fdcebf92aa2318a8d3656168da6ac1a5",
"reference": "78a0e288fdcebf92aa2318a8d3656168da6ac1a5",
"url": "https://api.github.com/repos/composer/ca-bundle/zipball/9fdb22c2e97a614657716178093cd1da90a64aa8",
"reference": "9fdb22c2e97a614657716178093cd1da90a64aa8",
"shasum": ""
},
"require": {
@ -860,7 +860,7 @@
"support": {
"irc": "irc://irc.freenode.org/composer",
"issues": "https://github.com/composer/ca-bundle/issues",
"source": "https://github.com/composer/ca-bundle/tree/1.2.9"
"source": "https://github.com/composer/ca-bundle/tree/1.2.10"
},
"funding": [
{
@ -876,20 +876,20 @@
"type": "tidelift"
}
],
"time": "2021-01-12T12:10:35+00:00"
"time": "2021-06-07T13:58:28+00:00"
},
{
"name": "composer/composer",
"version": "2.0.14",
"version": "2.1.3",
"source": {
"type": "git",
"url": "https://github.com/composer/composer.git",
"reference": "92b2ccbef65292ba9f2004271ef47c7231e2eed5"
"reference": "fc5c4573aafce3a018eb7f1f8f91cea423970f2e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/composer/zipball/92b2ccbef65292ba9f2004271ef47c7231e2eed5",
"reference": "92b2ccbef65292ba9f2004271ef47c7231e2eed5",
"url": "https://api.github.com/repos/composer/composer/zipball/fc5c4573aafce3a018eb7f1f8f91cea423970f2e",
"reference": "fc5c4573aafce3a018eb7f1f8f91cea423970f2e",
"shasum": ""
},
"require": {
@ -924,7 +924,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0-dev"
"dev-master": "2.1-dev"
}
},
"autoload": {
@ -958,7 +958,7 @@
"support": {
"irc": "irc://irc.freenode.org/composer",
"issues": "https://github.com/composer/composer/issues",
"source": "https://github.com/composer/composer/tree/2.0.14"
"source": "https://github.com/composer/composer/tree/2.1.3"
},
"funding": [
{
@ -974,7 +974,7 @@
"type": "tidelift"
}
],
"time": "2021-05-21T15:03:37+00:00"
"time": "2021-06-09T14:31:20+00:00"
},
{
"name": "composer/metadata-minifier",
@ -2219,16 +2219,16 @@
},
{
"name": "google/apiclient",
"version": "v2.9.1",
"version": "v2.9.2",
"source": {
"type": "git",
"url": "https://github.com/googleapis/google-api-php-client.git",
"reference": "2fb6e702aca5d68203fa737f89f6f774022494c6"
"reference": "e9ef4c26a044b8d39a46bcf296be795fe24a1849"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/googleapis/google-api-php-client/zipball/2fb6e702aca5d68203fa737f89f6f774022494c6",
"reference": "2fb6e702aca5d68203fa737f89f6f774022494c6",
"url": "https://api.github.com/repos/googleapis/google-api-php-client/zipball/e9ef4c26a044b8d39a46bcf296be795fe24a1849",
"reference": "e9ef4c26a044b8d39a46bcf296be795fe24a1849",
"shasum": ""
},
"require": {
@ -2243,7 +2243,7 @@
},
"require-dev": {
"cache/filesystem-adapter": "^0.3.2|^1.1",
"composer/composer": "^1.10",
"composer/composer": "^1.10.22",
"dealerdirect/phpcodesniffer-composer-installer": "^0.7",
"phpcompatibility/php-compatibility": "^9.2",
"phpunit/phpunit": "^5.7||^8.5.13",
@ -2282,29 +2282,29 @@
],
"support": {
"issues": "https://github.com/googleapis/google-api-php-client/issues",
"source": "https://github.com/googleapis/google-api-php-client/tree/v2.9.1"
"source": "https://github.com/googleapis/google-api-php-client/tree/v2.9.2"
},
"time": "2021-01-19T17:48:59+00:00"
"time": "2021-06-09T22:15:08+00:00"
},
{
"name": "google/apiclient-services",
"version": "v0.177.0",
"version": "v0.181.0",
"source": {
"type": "git",
"url": "https://github.com/googleapis/google-api-php-client-services.git",
"reference": "316cbf9b02c575a140d8cbeca48a3ca0070fcd5a"
"reference": "a4ea5fd96887d654d10d446b239e1ff60240e2c1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/316cbf9b02c575a140d8cbeca48a3ca0070fcd5a",
"reference": "316cbf9b02c575a140d8cbeca48a3ca0070fcd5a",
"url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/a4ea5fd96887d654d10d446b239e1ff60240e2c1",
"reference": "a4ea5fd96887d654d10d446b239e1ff60240e2c1",
"shasum": ""
},
"require": {
"php": ">=5.4"
"php": ">=5.6"
},
"require-dev": {
"phpunit/phpunit": "^4.8|^5"
"phpunit/phpunit": "^5.7||^8.5.13"
},
"type": "library",
"autoload": {
@ -2323,9 +2323,9 @@
],
"support": {
"issues": "https://github.com/googleapis/google-api-php-client-services/issues",
"source": "https://github.com/googleapis/google-api-php-client-services/tree/v0.177.0"
"source": "https://github.com/googleapis/google-api-php-client-services/tree/v0.181.0"
},
"time": "2021-05-15T11:18:02+00:00"
"time": "2021-06-13T11:20:02+00:00"
},
{
"name": "google/auth",
@ -3062,16 +3062,16 @@
},
{
"name": "laravel/framework",
"version": "v8.44.0",
"version": "v8.46.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
"reference": "7b3b27dc8911ab02a69731af2ba97b5130b2ddb8"
"reference": "a18266c612e0e6aba5e0174b3c873d2d217dccfb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/7b3b27dc8911ab02a69731af2ba97b5130b2ddb8",
"reference": "7b3b27dc8911ab02a69731af2ba97b5130b2ddb8",
"url": "https://api.github.com/repos/laravel/framework/zipball/a18266c612e0e6aba5e0174b3c873d2d217dccfb",
"reference": "a18266c612e0e6aba5e0174b3c873d2d217dccfb",
"shasum": ""
},
"require": {
@ -3153,7 +3153,7 @@
"orchestra/testbench-core": "^6.8",
"pda/pheanstalk": "^4.0",
"phpunit/phpunit": "^8.5.8|^9.3.3",
"predis/predis": "^1.1.1",
"predis/predis": "^1.1.2",
"symfony/cache": "^5.1.4"
},
"suggest": {
@ -3226,7 +3226,7 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
"time": "2021-05-27T16:46:06+00:00"
"time": "2021-06-08T13:36:46+00:00"
},
{
"name": "laravel/slack-notification-channel",
@ -4067,23 +4067,23 @@
},
{
"name": "league/omnipay",
"version": "v3.2.0",
"version": "v3.2.1",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/omnipay.git",
"reference": "0caded2a46f809dc42e350e07e798cc3c45cd47f"
"reference": "38f66a0cc043ed51d6edf7956d6439a2f263501f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/omnipay/zipball/0caded2a46f809dc42e350e07e798cc3c45cd47f",
"reference": "0caded2a46f809dc42e350e07e798cc3c45cd47f",
"url": "https://api.github.com/repos/thephpleague/omnipay/zipball/38f66a0cc043ed51d6edf7956d6439a2f263501f",
"reference": "38f66a0cc043ed51d6edf7956d6439a2f263501f",
"shasum": ""
},
"require": {
"omnipay/common": "^3.1",
"php": "^7.3|^8.0",
"php-http/discovery": "^1.12",
"php-http/guzzle7-adapter": "^0.1"
"php": "^7.2|^8.0",
"php-http/discovery": "^1.14",
"php-http/guzzle7-adapter": "^1"
},
"require-dev": {
"omnipay/tests": "^3|^4"
@ -4118,7 +4118,7 @@
],
"support": {
"issues": "https://github.com/thephpleague/omnipay/issues",
"source": "https://github.com/thephpleague/omnipay/tree/v3.2.0"
"source": "https://github.com/thephpleague/omnipay/tree/v3.2.1"
},
"funding": [
{
@ -4126,7 +4126,7 @@
"type": "github"
}
],
"time": "2021-06-01T09:16:20+00:00"
"time": "2021-06-05T11:34:12+00:00"
},
{
"name": "livewire/livewire",
@ -4570,16 +4570,16 @@
},
{
"name": "nesbot/carbon",
"version": "2.48.1",
"version": "2.49.0",
"source": {
"type": "git",
"url": "https://github.com/briannesbitt/Carbon.git",
"reference": "8d1f50f1436fb4b05e7127360483dd9c6e73da16"
"reference": "93d9db91c0235c486875d22f1e08b50bdf3e6eee"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/8d1f50f1436fb4b05e7127360483dd9c6e73da16",
"reference": "8d1f50f1436fb4b05e7127360483dd9c6e73da16",
"url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/93d9db91c0235c486875d22f1e08b50bdf3e6eee",
"reference": "93d9db91c0235c486875d22f1e08b50bdf3e6eee",
"shasum": ""
},
"require": {
@ -4659,7 +4659,7 @@
"type": "tidelift"
}
],
"time": "2021-05-26T22:08:38+00:00"
"time": "2021-06-02T07:31:40+00:00"
},
{
"name": "nikic/php-parser",
@ -4877,29 +4877,29 @@
},
{
"name": "omnipay/common",
"version": "v3.1.0",
"version": "v3.1.2",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/omnipay-common.git",
"reference": "fae2bc97a1b6c808361c5cafc796d380130714c7"
"reference": "5b16387ec5ab1b9ff86bdf0f20415088693b9948"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/omnipay-common/zipball/fae2bc97a1b6c808361c5cafc796d380130714c7",
"reference": "fae2bc97a1b6c808361c5cafc796d380130714c7",
"url": "https://api.github.com/repos/thephpleague/omnipay-common/zipball/5b16387ec5ab1b9ff86bdf0f20415088693b9948",
"reference": "5b16387ec5ab1b9ff86bdf0f20415088693b9948",
"shasum": ""
},
"require": {
"moneyphp/money": "^3.1",
"php": "^7.3|^8",
"php": "^7.2|^8",
"php-http/client-implementation": "^1",
"php-http/discovery": "^1.13",
"php-http/guzzle7-adapter": "^0.1",
"php-http/discovery": "^1.14",
"php-http/message": "^1.5",
"symfony/http-foundation": "^2.1|^3|^4|^5"
},
"require-dev": {
"omnipay/tests": "^4",
"omnipay/tests": "^4.1",
"php-http/guzzle7-adapter": "^1",
"php-http/mock-client": "^1",
"squizlabs/php_codesniffer": "^3.5"
},
@ -4909,7 +4909,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.0.x-dev"
"dev-master": "3.1.x-dev"
}
},
"autoload": {
@ -4957,7 +4957,7 @@
],
"support": {
"issues": "https://github.com/thephpleague/omnipay-common/issues",
"source": "https://github.com/thephpleague/omnipay-common/tree/v3.1.0"
"source": "https://github.com/thephpleague/omnipay-common/tree/v3.1.2"
},
"funding": [
{
@ -4965,7 +4965,7 @@
"type": "github"
}
],
"time": "2021-06-01T08:17:22+00:00"
"time": "2021-06-05T11:36:12+00:00"
},
{
"name": "omnipay/paypal",
@ -5358,16 +5358,16 @@
},
{
"name": "php-http/guzzle7-adapter",
"version": "0.1.1",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-http/guzzle7-adapter.git",
"reference": "1967de656b9679a2a6a66d0e4e16fa99bbed1ad1"
"reference": "fb075a71dbfa4847cf0c2938c4e5a9c478ef8b01"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-http/guzzle7-adapter/zipball/1967de656b9679a2a6a66d0e4e16fa99bbed1ad1",
"reference": "1967de656b9679a2a6a66d0e4e16fa99bbed1ad1",
"url": "https://api.github.com/repos/php-http/guzzle7-adapter/zipball/fb075a71dbfa4847cf0c2938c4e5a9c478ef8b01",
"reference": "fb075a71dbfa4847cf0c2938c4e5a9c478ef8b01",
"shasum": ""
},
"require": {
@ -5414,9 +5414,9 @@
],
"support": {
"issues": "https://github.com/php-http/guzzle7-adapter/issues",
"source": "https://github.com/php-http/guzzle7-adapter/tree/0.1.1"
"source": "https://github.com/php-http/guzzle7-adapter/tree/1.0.0"
},
"time": "2020-10-21T17:30:51+00:00"
"time": "2021-03-09T07:35:15+00:00"
},
{
"name": "php-http/httplug",
@ -5736,16 +5736,16 @@
},
{
"name": "phpseclib/phpseclib",
"version": "3.0.8",
"version": "3.0.9",
"source": {
"type": "git",
"url": "https://github.com/phpseclib/phpseclib.git",
"reference": "d9615a6fb970d9933866ca8b4036ec3407b020b6"
"reference": "a127a5133804ff2f47ae629dd529b129da616ad7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/d9615a6fb970d9933866ca8b4036ec3407b020b6",
"reference": "d9615a6fb970d9933866ca8b4036ec3407b020b6",
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/a127a5133804ff2f47ae629dd529b129da616ad7",
"reference": "a127a5133804ff2f47ae629dd529b129da616ad7",
"shasum": ""
},
"require": {
@ -5827,7 +5827,7 @@
],
"support": {
"issues": "https://github.com/phpseclib/phpseclib/issues",
"source": "https://github.com/phpseclib/phpseclib/tree/3.0.8"
"source": "https://github.com/phpseclib/phpseclib/tree/3.0.9"
},
"funding": [
{
@ -5843,7 +5843,7 @@
"type": "tidelift"
}
],
"time": "2021-04-19T03:20:48+00:00"
"time": "2021-06-14T06:54:45+00:00"
},
{
"name": "pragmarx/google2fa",
@ -7198,16 +7198,16 @@
},
{
"name": "stripe/stripe-php",
"version": "v7.80.0",
"version": "v7.83.0",
"source": {
"type": "git",
"url": "https://github.com/stripe/stripe-php.git",
"reference": "566900968407302f88a925ba731c87c05fe98a7a"
"reference": "bb7244c7334ad8bf30d31bb4972d5aff57df1563"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/stripe/stripe-php/zipball/566900968407302f88a925ba731c87c05fe98a7a",
"reference": "566900968407302f88a925ba731c87c05fe98a7a",
"url": "https://api.github.com/repos/stripe/stripe-php/zipball/bb7244c7334ad8bf30d31bb4972d5aff57df1563",
"reference": "bb7244c7334ad8bf30d31bb4972d5aff57df1563",
"shasum": ""
},
"require": {
@ -7253,9 +7253,9 @@
],
"support": {
"issues": "https://github.com/stripe/stripe-php/issues",
"source": "https://github.com/stripe/stripe-php/tree/v7.80.0"
"source": "https://github.com/stripe/stripe-php/tree/v7.83.0"
},
"time": "2021-05-26T19:10:43+00:00"
"time": "2021-06-07T23:15:45+00:00"
},
{
"name": "swiftmailer/swiftmailer",
@ -8085,16 +8085,16 @@
},
{
"name": "symfony/http-foundation",
"version": "v5.3.0",
"version": "v5.3.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-foundation.git",
"reference": "31f25d99b329a1461f42bcef8505b54926a30be6"
"reference": "8827b90cf8806e467124ad476acd15216c2fceb6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/31f25d99b329a1461f42bcef8505b54926a30be6",
"reference": "31f25d99b329a1461f42bcef8505b54926a30be6",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/8827b90cf8806e467124ad476acd15216c2fceb6",
"reference": "8827b90cf8806e467124ad476acd15216c2fceb6",
"shasum": ""
},
"require": {
@ -8138,7 +8138,7 @@
"description": "Defines an object-oriented layer for the HTTP specification",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/http-foundation/tree/v5.3.0"
"source": "https://github.com/symfony/http-foundation/tree/v5.3.1"
},
"funding": [
{
@ -8154,20 +8154,20 @@
"type": "tidelift"
}
],
"time": "2021-05-26T17:43:10+00:00"
"time": "2021-06-02T09:32:00+00:00"
},
{
"name": "symfony/http-kernel",
"version": "v5.3.0",
"version": "v5.3.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-kernel.git",
"reference": "f8e8f5391b6909e2f0ba8c12220ab7af3050eb4f"
"reference": "74eb022e3bac36b3d3a897951a98759f2b32b864"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/f8e8f5391b6909e2f0ba8c12220ab7af3050eb4f",
"reference": "f8e8f5391b6909e2f0ba8c12220ab7af3050eb4f",
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/74eb022e3bac36b3d3a897951a98759f2b32b864",
"reference": "74eb022e3bac36b3d3a897951a98759f2b32b864",
"shasum": ""
},
"require": {
@ -8250,7 +8250,7 @@
"description": "Provides a structured process for converting a Request into a Response",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/http-kernel/tree/v5.3.0"
"source": "https://github.com/symfony/http-kernel/tree/v5.3.1"
},
"funding": [
{
@ -8266,7 +8266,7 @@
"type": "tidelift"
}
],
"time": "2021-05-31T10:44:03+00:00"
"time": "2021-06-02T10:07:12+00:00"
},
{
"name": "symfony/mime",
@ -10355,6 +10355,57 @@
},
"time": "2019-07-12T14:06:05+00:00"
},
{
"name": "wepay/php-sdk",
"version": "0.3.1",
"source": {
"type": "git",
"url": "https://github.com/wepay/PHP-SDK.git",
"reference": "2a89ceb2954d117d082f869d3bfcb7864e6c2a7d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/wepay/PHP-SDK/zipball/2a89ceb2954d117d082f869d3bfcb7864e6c2a7d",
"reference": "2a89ceb2954d117d082f869d3bfcb7864e6c2a7d",
"shasum": ""
},
"require": {
"ext-curl": "*",
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "0.2.x-dev"
}
},
"autoload": {
"classmap": [
"wepay.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "WePay",
"email": "api@wepay.com"
}
],
"description": "WePay APIv2 SDK for PHP",
"keywords": [
"payment",
"sdk",
"wepay"
],
"support": {
"issues": "https://github.com/wepay/PHP-SDK/issues",
"source": "https://github.com/wepay/PHP-SDK/tree/master"
},
"time": "2017-01-21T07:03:26+00:00"
},
{
"name": "wildbit/swiftmailer-postmark",
"version": "3.3.0",
@ -10693,16 +10744,16 @@
},
{
"name": "barryvdh/laravel-debugbar",
"version": "v3.6.0",
"version": "v3.6.1",
"source": {
"type": "git",
"url": "https://github.com/barryvdh/laravel-debugbar.git",
"reference": "bc99f4c52aec0636ecb3aae4576ce84c5773bae2"
"reference": "f6f0f895a33cac801286a74355d146bb5384a5da"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/bc99f4c52aec0636ecb3aae4576ce84c5773bae2",
"reference": "bc99f4c52aec0636ecb3aae4576ce84c5773bae2",
"url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/f6f0f895a33cac801286a74355d146bb5384a5da",
"reference": "f6f0f895a33cac801286a74355d146bb5384a5da",
"shasum": ""
},
"require": {
@ -10762,7 +10813,7 @@
],
"support": {
"issues": "https://github.com/barryvdh/laravel-debugbar/issues",
"source": "https://github.com/barryvdh/laravel-debugbar/tree/v3.6.0"
"source": "https://github.com/barryvdh/laravel-debugbar/tree/v3.6.1"
},
"funding": [
{
@ -10770,7 +10821,7 @@
"type": "github"
}
],
"time": "2021-06-01T08:46:17+00:00"
"time": "2021-06-02T06:42:22+00:00"
},
{
"name": "brianium/paratest",
@ -11263,16 +11314,16 @@
},
{
"name": "facade/ignition",
"version": "2.9.0",
"version": "2.10.2",
"source": {
"type": "git",
"url": "https://github.com/facade/ignition.git",
"reference": "e7db3b601ce742568b92648818ef903904d20164"
"reference": "43688227bbf27c43bc1ad83af224f135b6ef0ff4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/facade/ignition/zipball/e7db3b601ce742568b92648818ef903904d20164",
"reference": "e7db3b601ce742568b92648818ef903904d20164",
"url": "https://api.github.com/repos/facade/ignition/zipball/43688227bbf27c43bc1ad83af224f135b6ef0ff4",
"reference": "43688227bbf27c43bc1ad83af224f135b6ef0ff4",
"shasum": ""
},
"require": {
@ -11336,7 +11387,7 @@
"issues": "https://github.com/facade/ignition/issues",
"source": "https://github.com/facade/ignition"
},
"time": "2021-05-05T06:45:12+00:00"
"time": "2021-06-11T06:57:25+00:00"
},
{
"name": "facade/ignition-contracts",
@ -11393,20 +11444,20 @@
},
{
"name": "felixfbecker/advanced-json-rpc",
"version": "v3.2.0",
"version": "v3.2.1",
"source": {
"type": "git",
"url": "https://github.com/felixfbecker/php-advanced-json-rpc.git",
"reference": "06f0b06043c7438959dbdeed8bb3f699a19be22e"
"reference": "b5f37dbff9a8ad360ca341f3240dc1c168b45447"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/felixfbecker/php-advanced-json-rpc/zipball/06f0b06043c7438959dbdeed8bb3f699a19be22e",
"reference": "06f0b06043c7438959dbdeed8bb3f699a19be22e",
"url": "https://api.github.com/repos/felixfbecker/php-advanced-json-rpc/zipball/b5f37dbff9a8ad360ca341f3240dc1c168b45447",
"reference": "b5f37dbff9a8ad360ca341f3240dc1c168b45447",
"shasum": ""
},
"require": {
"netresearch/jsonmapper": "^1.0 || ^2.0",
"netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0",
"php": "^7.1 || ^8.0",
"phpdocumentor/reflection-docblock": "^4.3.4 || ^5.0.0"
},
@ -11432,9 +11483,9 @@
"description": "A more advanced JSONRPC implementation",
"support": {
"issues": "https://github.com/felixfbecker/php-advanced-json-rpc/issues",
"source": "https://github.com/felixfbecker/php-advanced-json-rpc/tree/v3.2.0"
"source": "https://github.com/felixfbecker/php-advanced-json-rpc/tree/v3.2.1"
},
"time": "2021-01-10T17:48:47+00:00"
"time": "2021-06-11T22:34:44+00:00"
},
{
"name": "felixfbecker/language-server-protocol",
@ -11494,16 +11545,16 @@
},
{
"name": "filp/whoops",
"version": "2.12.1",
"version": "2.13.0",
"source": {
"type": "git",
"url": "https://github.com/filp/whoops.git",
"reference": "c13c0be93cff50f88bbd70827d993026821914dd"
"reference": "2edbc73a4687d9085c8f20f398eebade844e8424"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/filp/whoops/zipball/c13c0be93cff50f88bbd70827d993026821914dd",
"reference": "c13c0be93cff50f88bbd70827d993026821914dd",
"url": "https://api.github.com/repos/filp/whoops/zipball/2edbc73a4687d9085c8f20f398eebade844e8424",
"reference": "2edbc73a4687d9085c8f20f398eebade844e8424",
"shasum": ""
},
"require": {
@ -11553,7 +11604,7 @@
],
"support": {
"issues": "https://github.com/filp/whoops/issues",
"source": "https://github.com/filp/whoops/tree/2.12.1"
"source": "https://github.com/filp/whoops/tree/2.13.0"
},
"funding": [
{
@ -11561,7 +11612,7 @@
"type": "github"
}
],
"time": "2021-04-25T12:00:00+00:00"
"time": "2021-06-04T12:00:00+00:00"
},
{
"name": "friendsofphp/php-cs-fixer",
@ -11920,16 +11971,16 @@
},
{
"name": "netresearch/jsonmapper",
"version": "v2.1.0",
"version": "v4.0.0",
"source": {
"type": "git",
"url": "https://github.com/cweiske/jsonmapper.git",
"reference": "e0f1e33a71587aca81be5cffbb9746510e1fe04e"
"reference": "8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/e0f1e33a71587aca81be5cffbb9746510e1fe04e",
"reference": "e0f1e33a71587aca81be5cffbb9746510e1fe04e",
"url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d",
"reference": "8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d",
"shasum": ""
},
"require": {
@ -11937,10 +11988,10 @@
"ext-pcre": "*",
"ext-reflection": "*",
"ext-spl": "*",
"php": ">=5.6"
"php": ">=7.1"
},
"require-dev": {
"phpunit/phpunit": "~4.8.35 || ~5.7 || ~6.4 || ~7.0",
"phpunit/phpunit": "~7.5 || ~8.0 || ~9.0",
"squizlabs/php_codesniffer": "~3.5"
},
"type": "library",
@ -11965,9 +12016,9 @@
"support": {
"email": "cweiske@cweiske.de",
"issues": "https://github.com/cweiske/jsonmapper/issues",
"source": "https://github.com/cweiske/jsonmapper/tree/master"
"source": "https://github.com/cweiske/jsonmapper/tree/v4.0.0"
},
"time": "2020-04-16T18:48:43+00:00"
"time": "2020-12-01T19:48:11+00:00"
},
{
"name": "nunomaduro/collision",
@ -12821,16 +12872,16 @@
},
{
"name": "phpunit/phpunit",
"version": "9.5.4",
"version": "9.5.5",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "c73c6737305e779771147af66c96ca6a7ed8a741"
"reference": "89ff45ea9d70e35522fb6654a2ebc221158de276"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c73c6737305e779771147af66c96ca6a7ed8a741",
"reference": "c73c6737305e779771147af66c96ca6a7ed8a741",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/89ff45ea9d70e35522fb6654a2ebc221158de276",
"reference": "89ff45ea9d70e35522fb6654a2ebc221158de276",
"shasum": ""
},
"require": {
@ -12860,7 +12911,7 @@
"sebastian/global-state": "^5.0.1",
"sebastian/object-enumerator": "^4.0.3",
"sebastian/resource-operations": "^3.0.3",
"sebastian/type": "^2.3",
"sebastian/type": "^2.3.2",
"sebastian/version": "^3.0.2"
},
"require-dev": {
@ -12908,7 +12959,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.4"
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.5"
},
"funding": [
{
@ -12920,7 +12971,7 @@
"type": "github"
}
],
"time": "2021-03-23T07:16:29+00:00"
"time": "2021-06-05T04:49:07+00:00"
},
{
"name": "sebastian/cli-parser",
@ -13428,16 +13479,16 @@
},
{
"name": "sebastian/global-state",
"version": "5.0.2",
"version": "5.0.3",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/global-state.git",
"reference": "a90ccbddffa067b51f574dea6eb25d5680839455"
"reference": "23bd5951f7ff26f12d4e3242864df3e08dec4e49"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/a90ccbddffa067b51f574dea6eb25d5680839455",
"reference": "a90ccbddffa067b51f574dea6eb25d5680839455",
"url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/23bd5951f7ff26f12d4e3242864df3e08dec4e49",
"reference": "23bd5951f7ff26f12d4e3242864df3e08dec4e49",
"shasum": ""
},
"require": {
@ -13480,7 +13531,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/global-state/issues",
"source": "https://github.com/sebastianbergmann/global-state/tree/5.0.2"
"source": "https://github.com/sebastianbergmann/global-state/tree/5.0.3"
},
"funding": [
{
@ -13488,7 +13539,7 @@
"type": "github"
}
],
"time": "2020-10-26T15:55:19+00:00"
"time": "2021-06-11T13:31:12+00:00"
},
{
"name": "sebastian/lines-of-code",
@ -13779,16 +13830,16 @@
},
{
"name": "sebastian/type",
"version": "2.3.1",
"version": "2.3.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/type.git",
"reference": "81cd61ab7bbf2de744aba0ea61fae32f721df3d2"
"reference": "0d1c587401514d17e8f9258a27e23527cb1b06c1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/type/zipball/81cd61ab7bbf2de744aba0ea61fae32f721df3d2",
"reference": "81cd61ab7bbf2de744aba0ea61fae32f721df3d2",
"url": "https://api.github.com/repos/sebastianbergmann/type/zipball/0d1c587401514d17e8f9258a27e23527cb1b06c1",
"reference": "0d1c587401514d17e8f9258a27e23527cb1b06c1",
"shasum": ""
},
"require": {
@ -13823,7 +13874,7 @@
"homepage": "https://github.com/sebastianbergmann/type",
"support": {
"issues": "https://github.com/sebastianbergmann/type/issues",
"source": "https://github.com/sebastianbergmann/type/tree/2.3.1"
"source": "https://github.com/sebastianbergmann/type/tree/2.3.2"
},
"funding": [
{
@ -13831,7 +13882,7 @@
"type": "github"
}
],
"time": "2020-10-26T13:18:59+00:00"
"time": "2021-06-04T13:02:07+00:00"
},
{
"name": "sebastian/version",
@ -13888,16 +13939,16 @@
},
{
"name": "swagger-api/swagger-ui",
"version": "v3.49.0",
"version": "v3.50.0",
"source": {
"type": "git",
"url": "https://github.com/swagger-api/swagger-ui.git",
"reference": "bbfa31beae18df5118dfcdfec257bd64dc8f61a2"
"reference": "91858cc811d3cddb45ef604365e2c88cd96e4ca0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/swagger-api/swagger-ui/zipball/bbfa31beae18df5118dfcdfec257bd64dc8f61a2",
"reference": "bbfa31beae18df5118dfcdfec257bd64dc8f61a2",
"url": "https://api.github.com/repos/swagger-api/swagger-ui/zipball/91858cc811d3cddb45ef604365e2c88cd96e4ca0",
"reference": "91858cc811d3cddb45ef604365e2c88cd96e4ca0",
"shasum": ""
},
"type": "library",
@ -13943,9 +13994,9 @@
],
"support": {
"issues": "https://github.com/swagger-api/swagger-ui/issues",
"source": "https://github.com/swagger-api/swagger-ui/tree/v3.49.0"
"source": "https://github.com/swagger-api/swagger-ui/tree/v3.50.0"
},
"time": "2021-05-19T21:54:39+00:00"
"time": "2021-06-03T21:00:28+00:00"
},
{
"name": "symfony/debug",

View File

@ -148,6 +148,11 @@ return [
'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),
'wepay' => [
'environment' => env('WEPAY_ENVIRONMENT', 'stage'),
'client_id' => env('WEPAY_CLIENT_ID', ''),
'client_secret' => env('WEPAY_CLIENT_SECRET',''),
],
'ninja_stripe_publishable_key' => env('NINJA_PUBLISHABLE_KEY', null),
'ninja_stripe_client_id' => env('NINJA_STRIPE_CLIENT_ID', null),
'ninja_default_company_id' => env('NINJA_COMPANY_ID', null),

View File

@ -0,0 +1,31 @@
<?php
use App\Models\Gateway;
use App\Utils\Ninja;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class ActivateWePay extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
if(Gateway::count() >=1 && Ninja::isHosted())
Gateway::whereIn('id', [49])->update(['visible' => true]);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
}
}

View File

@ -101,8 +101,9 @@ class PaymentLibrariesSeeder extends Seeder
if (Ninja::isHosted()) {
Gateway::whereIn('id', [20])->update(['visible' => 0]);
Gateway::whereIn('id', [56])->update(['visible' => 1]);
Gateway::whereIn('id', [49])->update(['visible' => 1]);
}
Gateway::all()->each(function ($gateway) {
$gateway->site_url = $gateway->getHelp();
$gateway->save();

383
public/images/wepay.svg Normal file
View File

@ -0,0 +1,383 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="146.57001"
height="53.70924"
viewBox="0 0 38.779979 14.21057"
version="1.1"
id="svg3260"
inkscape:version="0.92.0 r15299"
sodipodi:docname="wepay.svg">
<defs
id="defs3254">
<clipPath
id="clipPath3278"
clipPathUnits="userSpaceOnUse">
<path
inkscape:connector-curvature="0"
id="path3276"
d="M 0,0 H 595.276 V 841.89 H 0 Z" />
</clipPath>
<clipPath
id="clipPath3288"
clipPathUnits="userSpaceOnUse">
<path
inkscape:connector-curvature="0"
id="path3286"
d="M 0,841.89 H 595.276 V 0 H 0 Z" />
</clipPath>
<clipPath
id="clipPath3318"
clipPathUnits="userSpaceOnUse">
<path
inkscape:connector-curvature="0"
id="path3316"
d="M 0,841.89 H 595.276 V 0 H 0 Z" />
</clipPath>
<clipPath
id="clipPath3326"
clipPathUnits="userSpaceOnUse">
<path
inkscape:connector-curvature="0"
id="path3324"
d="M 419.528,14.172 H 581.103 V 31.18 H 419.528 Z" />
</clipPath>
<clipPath
id="clipPath3340"
clipPathUnits="userSpaceOnUse">
<path
inkscape:connector-curvature="0"
id="path3338"
d="M 419.528,14.172 H 581.102 V 31.18 H 419.528 Z" />
</clipPath>
<clipPath
id="clipPath4412"
clipPathUnits="userSpaceOnUse">
<path
inkscape:connector-curvature="0"
id="path4410"
d="M 0,0 H 598.441 V 612 H 0 Z" />
</clipPath>
<clipPath
id="clipPath4436"
clipPathUnits="userSpaceOnUse">
<path
inkscape:connector-curvature="0"
id="path4434"
d="m 536.641,119.187 h 220.051 v 64.601 H 536.641 Z" />
</clipPath>
<clipPath
id="clipPath4640"
clipPathUnits="userSpaceOnUse">
<path
inkscape:connector-curvature="0"
id="path4638"
d="M 0,0 H 612 V 792 H 0 Z" />
</clipPath>
<clipPath
id="clipPath4648"
clipPathUnits="userSpaceOnUse">
<path
inkscape:connector-curvature="0"
id="path4646"
d="M 35,523.946 H 577 V 98.36 H 35 Z" />
</clipPath>
<radialGradient
id="radialGradient4660"
spreadMethod="pad"
gradientTransform="matrix(533.4704,0,0,-533.4704,126.08601,504.87846)"
gradientUnits="userSpaceOnUse"
r="1"
cy="0"
cx="0"
fy="0"
fx="0">
<stop
id="stop4654"
offset="0"
style="stop-opacity:1;stop-color:#ffffff" />
<stop
id="stop4656"
offset="0.288571"
style="stop-opacity:1;stop-color:#ffffff" />
<stop
id="stop4658"
offset="1"
style="stop-opacity:1;stop-color:#f58345" />
</radialGradient>
<clipPath
id="clipPath4686"
clipPathUnits="userSpaceOnUse">
<path
inkscape:connector-curvature="0"
id="path4684"
d="M 0,0 H 612 V 792 H 0 Z" />
</clipPath>
<clipPath
id="clipPath4722"
clipPathUnits="userSpaceOnUse">
<path
inkscape:connector-curvature="0"
id="path4720"
d="M 0,0 H 612 V 792 H 0 Z" />
</clipPath>
<linearGradient
id="linearGradient4744"
spreadMethod="pad"
gradientTransform="matrix(540,0,0,-540,36,610.90001)"
gradientUnits="userSpaceOnUse"
y2="0"
x2="1"
y1="0"
x1="0">
<stop
id="stop4740"
offset="0"
style="stop-opacity:1;stop-color:#808285" />
<stop
id="stop4742"
offset="1"
style="stop-opacity:1;stop-color:#4f4f51" />
</linearGradient>
<clipPath
id="clipPath4766"
clipPathUnits="userSpaceOnUse">
<path
inkscape:connector-curvature="0"
id="path4764"
d="M 44.28,414.765 H 255.575 V 495 H 44.28 Z" />
</clipPath>
<clipPath
id="clipPath4896"
clipPathUnits="userSpaceOnUse">
<path
inkscape:connector-curvature="0"
id="path4894"
d="m 36.544,99.36 h 539.4 v 422.445 h -539.4 z" />
</clipPath>
<clipPath
id="clipPath4904"
clipPathUnits="userSpaceOnUse">
<path
inkscape:connector-curvature="0"
id="path4902"
d="M -32.9452,542.473 H 652.766 V -381.128 H -32.9452 Z" />
</clipPath>
<clipPath
id="clipPath4936"
clipPathUnits="userSpaceOnUse">
<path
inkscape:connector-curvature="0"
id="path4934"
d="M 139.265,232.92 H 474.15 V 389.039 H 139.265 Z" />
</clipPath>
<mask
id="mask4940"
height="1"
width="1"
y="0"
x="0"
maskUnits="userSpaceOnUse">
<image
id="image4942"
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAdIAAADbCAAAAAD8c0+WAAAAAXNCSVQI5gpbmQAABJtJREFUeJzt3U9om2UcwPGkia1Zy9YqbqMOrA5HRUZxeFE8yFAY8zAvevGgZ0GGePUgzKuXOS86xJPgn5NjgjBYRaH+ISdZD26zW8VtnX+2lrazSZN48CCzSZb3fZ8+Sb79fo7vm/yeh355m7xvYcvlJEmSJEmSJG0Z+YhrPXz87oir9Ynzb14PPDFe0r1HX9wVbbE+Mv/e+zeDDoyVtPjWq2ORluo73xxeDjmuEHJYG28cK0Vaqf88MHY65LhISXec9Bpt7fG/fgg4bSDgrDZefzDOOv0p/87TAafFSbrv5SjL9K27ngs4LE7SVyaiLNO/RgPOipO0EmWVPhbyhj3SZ6niMWlPGAn4fMCkPeHJkXCzTNoTSgE7mLQn1APOMimOSXFMimNSHJPimBTHpDgmxTEpjklxTIpjUhyT4pgUx6Q4JsUxKY5JcUyKY1Ick+KYFMekOCbFMSmOSXFMimNSHJPimBTHpDgmxTEpjklxTIpjUhyT4pgUx6Q43UjaCPmv/Oj/unKVNrqx6JbRlaQx/yeTrcekOMUurGnRTeU3XhyT4pgUx6Q4JsUxKY5JcUyKY1Ick+KYFMekOCbFMSmOSXFMimNSHJPimBTHpDgmxTEpjklxTIpjUhyT4pgUx6Q4JsUxKY5JcUyKY1Ick+KYFMekOCbFMSmOSXFMimNSHJPimBTHpDgmxTEpjklxTIpjUhyT4pgUx6Q4JsUxKY5JcUyKY1Ick+KYFMekOCbFMSmOSXFMimNSHJPimBTHpDgmxTEpjklxTIpjUhyT4pgUx6Q4JsUxKY5JcUyKY1Ick+KYFMekOCbFMSmOSXFMimNSHJPimBTHpDgmxTEpjklxTIpjUhyT4pgUx6Q4JsUxKY5JcUyKY1Ick+KYFMekOCbFMSmOSXFMimNSHJPimBTHpDgmxTEpjklxTIpjUhyT4pgUx6Q4JsUxKY5JcUyKY1KcOEkrUVbpY42VcLOKnb6wNL57Z23k+ftSrTKR6l1byMjZ9XRv/G52/uertx/Kd/refLFYqI9/8Ui6lbVJXvuwXq3dfqjjq7RRreZyy8uht6RsllY3HEr2WdrxNa1ImhRJmLQQaCcKpEm/ZEnXLgTaicL4+/eNx5IlvfXVxUB7UQiV6V82Hkz2m7RxbW6lMu4nam+olj8+fmnjLX/SPIV9U0enSmG2pCxq66c+OrPW5ETS7zuNxUvXboxvD7EnZTJz6sRM06dyib/C1teuLq5Mbsu+JWWxXP7gs9l601NpPheH7j/07ORkti0pi4XpTy7+1GhxMs2NZu3GXPnK4J6OHzwpsPnP351eaHk23bOD1YXL51b/2D2Udk/K4Er55Kfn2pxPf0NSuvfwUwe3jaV+v5KrVa5/efn0b0u1di/Kco85ODj12EsPDQ9nGKEEKitnZs/+WK3e4WXZHhsMDN1z4NEXcrnCaGlnpkFqq7a0uJS/+fW331dutfpS9J/sT4LyjVwut//YkcyD1NL6ibf//PcH3YEAf1oZnXji0P4jPn3YRAPrOw5uLzR5RN9Mtqu0WBye2PXMngN7M01RJy7MzZTP/1qp3vFa/Qd4HqFCB+U7NwAAAABJRU5ErkJggg=="
preserveAspectRatio="none"
style="image-rendering:optimizeSpeed"
height="1"
width="1" />
</mask>
<style
id="style5609">.cls-1{fill:#508ecb;}.cls-2{fill:#32af4a;}.cls-3{fill:#231f20;}</style>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.509377"
inkscape:cx="72.784999"
inkscape:cy="26.354619"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
units="px"
inkscape:window-width="1280"
inkscape:window-height="744"
inkscape:window-x="-4"
inkscape:window-y="-4"
inkscape:window-maximized="1" />
<metadata
id="metadata3257">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-89.599445,-326.04814)">
<g
id="g5682"
transform="matrix(0.26458333,0,0,0.26458333,89.258134,325.73801)">
<path
d="m 27.08,27.23 h -2.6 L 19.1,10.51 H 19 L 13.63,27.23 H 11.08 L 1.79,2.39 h 5 l 5.52,16.14 H 12.4 L 17.53,2.39 h 3 l 5.16,16.14 h 0.09 L 31.3,2.39 h 5 z"
class="cls-1"
data-name="&lt;Compound Path&gt;"
id="_Compound_Path_"
inkscape:connector-curvature="0"
style="fill:#508ecb" />
<path
d="m 41.26,16.83 a 8.43,8.43 0 0 0 8.16,6.77 7.94,7.94 0 0 0 7.09,-4.17 h 4.66 a 12.66,12.66 0 0 1 -11.92,8.43 c -7,0 -12.65,-6.06 -12.65,-13.1 A 12.700946,12.700946 0 1 1 62,14.45 c 0,0.81 -0.09,2.06 -0.13,2.38 z m 16.06,-3.59 a 8.07,8.07 0 0 0 -16.06,0 z"
class="cls-1"
data-name="&lt;Compound Path&gt;"
id="_Compound_Path_2"
inkscape:connector-curvature="0"
style="fill:#508ecb" />
<path
d="m 65.52,2.39 h 4.26 V 6.2 h 0.09 a 10.49,10.49 0 0 1 8.84,-4.48 c 8.43,0 12.86,6.77 12.86,13.4 A 12.52,12.52 0 0 1 79,27.86 c -5.2,0 -7.8,-2.65 -8.74,-4.22 H 70.17 V 35.53 H 65.52 Z M 78.39,6 C 73.5,6 70.1,10.21 70.1,14.78 a 8.41,8.41 0 1 0 16.81,0.09 C 86.91,10.42 83.46,6 78.39,6 Z"
class="cls-2"
data-name="&lt;Compound Path&gt;"
id="_Compound_Path_3"
inkscape:connector-curvature="0"
style="fill:#32af4a" />
<path
d="m 115.56,27.23 v -3.86 h -0.09 A 9.5,9.5 0 0 1 107,27.86 12.75,12.75 0 0 1 93.82,14.86 C 93.82,8.81 98.08,1.68 107,1.68 a 9.82,9.82 0 0 1 8.43,4.17 h 0.09 V 2.39 h 4.67 V 27.23 Z M 107,6 c -5.74,0 -8.52,5 -8.52,8.87 a 8.57,8.57 0 1 0 17.13,0 A 8.48,8.48 0 0 0 107,6 Z"
class="cls-2"
data-name="&lt;Compound Path&gt;"
id="_Compound_Path_4"
inkscape:connector-curvature="0"
style="fill:#32af4a" />
<path
d="m 133.68,35.53 h -4.84 l 3.63,-8.79 -9.91,-24.35 h 5 l 7.31,19.28 7.49,-19.28 h 5 z"
class="cls-2"
data-name="&lt;Compound Path&gt;"
id="_Compound_Path_5"
inkscape:connector-curvature="0"
style="fill:#32af4a" />
<path
id="path5620"
d="m 78,41.32 a 0.46,0.46 0 0 0 -0.45,0.46 V 45 h 8.49 l -3.87,-3.68 z"
class="cls-3"
inkscape:connector-curvature="0"
style="fill:#231f20" />
<path
id="path5622"
d="M 86.4,46 A 0.47,0.47 0 0 0 85.94,45.54 H 82.72 V 54 l 3.68,-3.87 z"
class="cls-3"
inkscape:connector-curvature="0"
style="fill:#231f20" />
<path
id="path5624"
d="m 81.75,54.32 a 0.46,0.46 0 0 0 0.45,-0.46 v -3.22 h -8.5 l 3.87,3.68 z"
class="cls-3"
inkscape:connector-curvature="0"
style="fill:#231f20" />
<path
id="path5626"
d="m 73.39,49.67 a 0.47,0.47 0 0 0 0.46,0.46 h 3.22 v -8.5 l -3.68,3.87 z"
class="cls-3"
inkscape:connector-curvature="0"
style="fill:#231f20" />
<path
id="path5628"
d="M 34.44,42.87 V 46.8 H 29 v -3.93 h -2 v 9.9 h 2 v -4.25 h 5.47 v 4.25 h 2 v -9.9 z"
class="cls-3"
inkscape:connector-curvature="0"
style="fill:#231f20" />
<path
id="path5630"
d="m 60.93,42.87 v 9.89 h 8.74 L 68.56,51 h -5.63 v -2.5 h 5.46 v -1.66 h -5.46 v -2.27 h 5.62 l 1.09,-1.7 z"
class="cls-3"
inkscape:connector-curvature="0"
style="fill:#231f20" />
<path
id="path5632"
d="M 19.57,42.87 A 2.84,2.84 0 0 0 16.46,46 v 3.7 a 2.85,2.85 0 0 0 3.1,3.11 h 6.25 L 24.65,51 H 20 c -1,0 -1.43,-0.36 -1.43,-1.48 v -3.39 c 0,-1.09 0.36,-1.51 1.46,-1.51 h 4.67 l 1.11,-1.75 z"
class="cls-3"
inkscape:connector-curvature="0"
style="fill:#231f20" />
<path
id="path5634"
d="m 52.19,42.87 a 2.33,2.33 0 0 0 -2.42,2.57 v 0.47 a 2.34,2.34 0 0 0 2.36,2.67 h 4.14 c 0.43,0 0.78,0.07 0.78,0.8 v 0.84 c 0,0.65 -0.33,0.8 -0.79,0.8 h -5.45 l -1.12,1.74 h 6.7 a 2.41,2.41 0 0 0 2.71,-2.65 v -0.69 a 2.37,2.37 0 0 0 -2.6,-2.64 h -4 c -0.43,0 -0.74,-0.12 -0.74,-0.77 v -0.68 c 0,-0.56 0.21,-0.76 0.72,-0.76 h 5.19 l 1.09,-1.71 z"
class="cls-3"
inkscape:connector-curvature="0"
style="fill:#231f20" />
<path
id="path5636"
d="m 42.22,42.87 -4.69,9.9 h 2.21 l 0.92,-2.05 h 5.1 l 0.91,2.05 h 2.22 l -4.7,-9.9 z m 1,2.06 L 45,49 h -3.6 z"
class="cls-3"
inkscape:connector-curvature="0"
style="fill:#231f20" />
<path
id="path5638"
d="M 10.17,51.48 10,50.57 H 9.93 A 2.88,2.88 0 0 1 8.98,51.39 3,3 0 0 1 7.79,51.6 2.14,2.14 0 0 1 6.29,51.11 1.81,1.81 0 0 1 5.75,49.71 c 0,-1.29 1,-2 3.11,-2 h 1.08 v -0.4 A 1.6,1.6 0 0 0 9.62,46.2 1.34,1.34 0 0 0 8.62,45.84 4.18,4.18 0 0 0 6.81,46.33 L 6.51,45.59 A 4.48,4.48 0 0 1 8.64,45 a 2.5,2.5 0 0 1 1.7,0.5 2.14,2.14 0 0 1 0.55,1.64 v 4.37 z M 8,50.8 a 2,2 0 0 0 1.42,-0.5 1.83,1.83 0 0 0 0.52,-1.39 v -0.58 h -1 a 3.6,3.6 0 0 0 -1.67,0.36 1.08,1.08 0 0 0 -0.51,1 1,1 0 0 0 0.32,0.8 A 1.28,1.28 0 0 0 8,50.8 Z"
class="cls-3"
inkscape:connector-curvature="0"
style="fill:#231f20" />
<path
id="path5640"
d="m 94,51.6 a 2.75,2.75 0 0 1 -2.16,-0.86 3.51,3.51 0 0 1 -0.76,-2.42 3.67,3.67 0 0 1 0.77,-2.49 2.83,2.83 0 0 1 2.2,-0.83 4.9,4.9 0 0 1 0.93,0.1 3,3 0 0 1 0.72,0.24 l -0.3,0.82 a 4.13,4.13 0 0 0 -0.7,-0.21 2.88,2.88 0 0 0 -0.67,-0.09 c -1.3,0 -2,0.83 -2,2.5 a 3,3 0 0 0 0.47,1.81 1.67,1.67 0 0 0 1.42,0.63 4.3,4.3 0 0 0 1.64,-0.34 v 0.86 A 3.54,3.54 0 0 1 94,51.6 Z"
class="cls-3"
inkscape:connector-curvature="0"
style="fill:#231f20" />
<path
id="path5642"
d="M 102.57,48.27 A 3.52,3.52 0 0 1 101.78,50.72 2.77,2.77 0 0 1 99.6,51.6 3,3 0 0 1 98.07,51.2 2.71,2.71 0 0 1 97,50 4.08,4.08 0 0 1 96.64,48.23 3.54,3.54 0 0 1 97.42,45.79 2.75,2.75 0 0 1 99.64,45 a 2.72,2.72 0 0 1 2.14,0.9 3.56,3.56 0 0 1 0.79,2.37 z m -4.89,0 a 3,3 0 0 0 0.5,1.87 1.68,1.68 0 0 0 1.44,0.65 1.74,1.74 0 0 0 1.45,-0.64 3.05,3.05 0 0 0 0.49,-1.88 3,3 0 0 0 -0.49,-1.86 1.75,1.75 0 0 0 -1.46,-0.63 1.72,1.72 0 0 0 -1.44,0.62 3,3 0 0 0 -0.49,1.87 z"
class="cls-3"
inkscape:connector-curvature="0"
style="fill:#231f20" />
<path
id="path5644"
d="m 112.35,51.48 v -4.17 a 1.73,1.73 0 0 0 -0.33,-1.15 1.29,1.29 0 0 0 -1,-0.38 1.64,1.64 0 0 0 -1.34,0.52 2.47,2.47 0 0 0 -0.43,1.6 v 3.58 h -1 v -4.17 a 1.73,1.73 0 0 0 -0.33,-1.15 1.29,1.29 0 0 0 -1,-0.38 1.57,1.57 0 0 0 -1.34,0.55 3,3 0 0 0 -0.42,1.79 v 3.36 h -1 V 45.07 H 105 l 0.15,0.88 v 0 a 1.84,1.84 0 0 1 0.78,-0.73 2.29,2.29 0 0 1 1.12,-0.27 A 1.91,1.91 0 0 1 109,46 v 0 a 2,2 0 0 1 0.83,-0.79 2.5,2.5 0 0 1 1.24,-0.3 2.19,2.19 0 0 1 1.63,0.56 2.5,2.5 0 0 1 0.54,1.79 v 4.18 z"
class="cls-3"
inkscape:connector-curvature="0"
style="fill:#231f20" />
<path
id="path5646"
d="m 118.21,51.6 a 2.88,2.88 0 0 1 -1.15,-0.23 2.28,2.28 0 0 1 -0.87,-0.71 h -0.07 a 8,8 0 0 1 0.07,1.06 v 2.64 h -1 V 45.07 H 116 l 0.14,0.88 v 0 a 2.18,2.18 0 0 1 0.88,-0.76 2.58,2.58 0 0 1 1.14,-0.24 2.36,2.36 0 0 1 2,0.88 4.62,4.62 0 0 1 0,4.89 2.35,2.35 0 0 1 -1.95,0.88 z m -0.14,-5.82 a 1.75,1.75 0 0 0 -1.43,0.54 2.82,2.82 0 0 0 -0.45,1.73 v 0.22 a 3.18,3.18 0 0 0 0.45,1.93 1.73,1.73 0 0 0 1.45,0.59 1.5,1.5 0 0 0 1.3,-0.68 3.2,3.2 0 0 0 0.47,-1.85 3.1,3.1 0 0 0 -0.47,-1.84 1.53,1.53 0 0 0 -1.32,-0.64 z"
class="cls-3"
inkscape:connector-curvature="0"
style="fill:#231f20" />
<path
id="path5648"
d="m 126.41,51.48 -0.19,-0.91 v 0 a 2.88,2.88 0 0 1 -1,0.82 3,3 0 0 1 -1.19,0.21 2.14,2.14 0 0 1 -1.5,-0.49 1.81,1.81 0 0 1 -0.54,-1.4 c 0,-1.29 1,-2 3.11,-2 h 1.08 v -0.4 a 1.6,1.6 0 0 0 -0.32,-1.11 1.34,1.34 0 0 0 -1,-0.36 4.18,4.18 0 0 0 -1.81,0.49 l -0.3,-0.74 a 4.48,4.48 0 0 1 2.17,-0.55 2.5,2.5 0 0 1 1.7,0.5 2.14,2.14 0 0 1 0.55,1.64 v 4.37 z m -2.19,-0.68 a 2,2 0 0 0 1.42,-0.5 1.83,1.83 0 0 0 0.52,-1.39 v -0.58 h -1 a 3.6,3.6 0 0 0 -1.67,0.36 1.08,1.08 0 0 0 -0.51,1 1,1 0 0 0 0.32,0.8 1.28,1.28 0 0 0 0.92,0.31 z"
class="cls-3"
inkscape:connector-curvature="0"
style="fill:#231f20" />
<path
id="path5650"
d="m 133.42,51.48 v -4.15 a 1.72,1.72 0 0 0 -0.35,-1.17 1.49,1.49 0 0 0 -1.12,-0.38 1.84,1.84 0 0 0 -1.47,0.54 2.7,2.7 0 0 0 -0.47,1.8 v 3.36 h -1 v -6.41 h 0.79 L 130,46 v 0 a 2,2 0 0 1 0.84,-0.74 2.78,2.78 0 0 1 1.2,-0.26 2.44,2.44 0 0 1 1.74,0.56 2.38,2.38 0 0 1 0.59,1.79 v 4.18 z"
class="cls-3"
inkscape:connector-curvature="0"
style="fill:#231f20" />
<path
id="path5652"
d="m 135.28,45.07 h 1 l 1.4,3.66 a 12.69,12.69 0 0 1 0.57,1.8 h 0.05 c 0,-0.19 0.16,-0.53 0.32,-1 0.16,-0.47 0.69,-2 1.59,-4.44 h 1 l -2.76,7.3 a 3.74,3.74 0 0 1 -0.95,1.54 2,2 0 0 1 -1.34,0.45 3.71,3.71 0 0 1 -0.88,-0.1 V 53.5 a 3.14,3.14 0 0 0 0.72,0.07 1.47,1.47 0 0 0 1.42,-1.12 l 0.36,-0.91 z"
class="cls-3"
inkscape:connector-curvature="0"
style="fill:#231f20" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -4267,6 +4267,7 @@ $LANG = array(
'company_import_failure_subject' => 'Error importing :company',
'company_import_failure_body' => 'There was an error importing the company data, the error message was:',
'recurring_invoice_due_date' => 'Due Date',
'amount_cents' => 'Amount in pennies,pence or cents',
);
return $LANG;

View File

@ -10,11 +10,11 @@
<input type="hidden" name="customer" value="{{ $token->gateway_customer_reference }}">
<input type="hidden" name="source" value="{{ $token->token }}">
@component('portal.ninja2020.components.general.card-element', ['title' => '#1 ' . ctrans('texts.amount')])
@component('portal.ninja2020.components.general.card-element', ['title' => '#1 ' . ctrans('texts.amount_cents')])
<input type="text" name="transactions[]" class="w-full input" required data-cy="verification-1st">
@endcomponent
@component('portal.ninja2020.components.general.card-element', ['title' => '#2 ' . ctrans('texts.amount')])
@component('portal.ninja2020.components.general.card-element', ['title' => '#2 ' . ctrans('texts.amount_cents')])
<input type="text" name="transactions[]" class="w-full input" required data-cy="verification-2nd">
@endcomponent

View File

@ -0,0 +1,150 @@
@extends('portal.ninja2020.layout.payments', ['gateway_title' => ctrans('texts.credit_card'), 'card_title' => ctrans('texts.credit_card')])
@section('gateway_head')
<meta name="year-invalid" content="{{ ctrans('texts.year_invalid') }}">
<meta name="month-invalid" content="{{ ctrans('texts.month_invalid') }}">
<meta name="credit-card-invalid" content="{{ ctrans('texts.credit_card_invalid') }}">
<script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
<script src="{{ asset('js/clients/payments/card-js.min.js') }}"></script>
<link href="{{ asset('css/card-js.min.css') }}" rel="stylesheet" type="text/css">
@endsection
@section('gateway_content')
<form action="{{ route('client.payment_methods.store', ['method' => App\Models\GatewayType::CREDIT_CARD]) }}"
method="post" id="server_response">
@csrf
<input type="hidden" name="company_gateway_id" value="{{ $gateway->company_gateway->id }}">
<input type="hidden" name="payment_method_id" value="1">
<input type="hidden" name="gateway_response" id="gateway_response">
<input type="hidden" name="is_default" id="is_default">
<input type="hidden" name="credit_card_id" id="credit_card_id">
</form>
@if(!Request::isSecure())
<p class="alert alert-failure">{{ ctrans('texts.https_required') }}</p>
@endif
<div class="alert alert-failure mb-4" hidden id="errors"></div>
@component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.method')])
{{ ctrans('texts.credit_card') }}
@endcomponent
@include('portal.ninja2020.gateways.wepay.includes.credit_card')
@component('portal.ninja2020.gateways.includes.pay_now', ['id' => 'card_button'])
{{ ctrans('texts.add_payment_method') }}
@endcomponent
@endsection
@section('gateway_footer')
<script type="text/javascript" src="https://static.wepay.com/min/js/tokenization.4.latest.js"></script>
<script type="text/javascript">
(function() {
@if(config('ninja.wepay.environment') == 'staging')
WePay.set_endpoint("stage");
@else
WePay.set_endpoint("production");
@endif
// Shortcuts
var d = document;
d.id = d.getElementById,
valueById = function(id) {
return d.id(id).value;
};
// For those not using DOM libraries
var addEvent = function(e,v,f) {
if (!!window.attachEvent) { e.attachEvent('on' + v, f); }
else { e.addEventListener(v, f, false); }
};
let errors = document.getElementById('errors');
// Attach the event to the DOM
addEvent(document.getElementById('card_button'), 'click', function() {
var myCard = $('#my-card');
if(document.getElementById('cardholder_name') == "") {
document.getElementById('cardholder_name').focus();
errors.textContent = "Cardholder name required.";
errors.hidden = false;
return;
}
else if(myCard.CardJs('cardNumber') == ""){
document.getElementById('card_number').focus();
errors.textContent = "Card number required.";
errors.hidden = false;
return;
}
else if(myCard.CardJs('cvc') == ""){
document.getElementById('cvv').focus();
errors.textContent = "CVV number required.";
errors.hidden = false;
return;
}
else if(myCard.CardJs('expiryMonth') == ""){
// document.getElementById('expiry_month').focus();
errors.textContent = "Expiry Month number required.";
errors.hidden = false;
return;
}
else if(myCard.CardJs('expiryYear') == ""){
// document.getElementById('expiry_year').focus();
errors.textContent = "Expiry Year number required.";
errors.hidden = false;
return;
}
cardButton = document.getElementById('card_button');
cardButton.disabled = true;
cardButton.querySelector('svg').classList.remove('hidden');
cardButton.querySelector('span').classList.add('hidden');
var userName = [valueById('cardholder_name')].join(' ');
response = WePay.credit_card.create({
"client_id": "{{ config('ninja.wepay.client_id') }}",
"user_name": valueById('cardholder_name'),
"email": "{{ $contact->email }}",
"cc_number": myCard.CardJs('cardNumber'),
"cvv": myCard.CardJs('cvc'),
"expiration_month": myCard.CardJs('expiryMonth'),
"expiration_year": myCard.CardJs('expiryYear'),
"address": {
"postal_code": "{{ $contact->client->postal_code }}"
}
}, function(data) {
if (data.error) {
//console.log(data);
// handle error response error_description
cardButton = document.getElementById('card_button');
cardButton.disabled = false;
cardButton.querySelector('svg').classList.add('hidden');
cardButton.querySelector('span').classList.remove('hidden');
errors.textContent = '';
errors.textContent = data.error_description;
errors.hidden = false;
} else {
var token = data.credit_card_id;
document.querySelector('input[name="credit_card_id"]').value = token;
document.getElementById('server_response').submit();
}
});
});
})();
</script>
@endsection

View File

@ -0,0 +1,71 @@
@extends('portal.ninja2020.layout.payments', ['gateway_title' => ctrans('texts.credit_card'), 'card_title' => ctrans('texts.bank_transfer')])
@section('gateway_head')
@endsection
@section('gateway_content')
<form action="{{ route('client.payment_methods.store', ['method' => App\Models\GatewayType::BANK_TRANSFER]) }}"
method="post" id="server_response">
@csrf
<input type="hidden" name="company_gateway_id" value="{{ $gateway->company_gateway->id }}">
<input type="hidden" name="payment_method_id" value="2">
<input type="hidden" name="is_default" id="is_default">
<input type="hidden" name="bank_account_id" id="bank_account_id">
</form>
<div class="alert alert-failure mb-4" hidden id="errors"></div>
@endsection
@section('gateway_footer')
<script type="text/javascript" src="https://static.wepay.com/min/js/tokenization.4.latest.js"></script>
<script type="text/javascript">
(function() {
@if(config('ninja.wepay.environment') == 'staging')
WePay.set_endpoint("stage");
@else
WePay.set_endpoint("production");
@endif
window.onload = function(){
response = WePay.bank_account.create({
'client_id': "{{ config('ninja.wepay.client_id') }}",
'email': "{{ $contact->email }}"
}, function(data){
if(data.error) {
console.log("Pop-up closing: ")
errors.textContent = '';
errors.textContent = data.error_description;
errors.hidden = false;
} else {
// call your own app's API to save the token inside the data;
// show a success page
console.log(data);
document.querySelector('input[name="bank_account_id"]').value = data.bank_account_id;
document.getElementById('server_response').submit();
}
}, function(data){
if(data.error) {
console.log("Pop-up opening: ");
console.log(data);
// handle error response
errors.textContent = '';
errors.textContent = data.error_description;
errors.hidden = false;
} else {
// call your own app's API to save the token inside the data;
// show a success page
}
}
);
};
})();
</script>
@endsection

View File

@ -0,0 +1,24 @@
@extends('portal.ninja2020.layout.payments', ['gateway_title' => 'ACH (Verification)', 'card_title' => 'ACH (Verification)'])
@section('gateway_content')
@if(session()->has('error'))
<div class="alert alert-failure mb-4">{{ session('error') }}</div>
@endif
<form method="POST">
@csrf
<input type="hidden" name="token" value="{{ $token->token }}">
@component('portal.ninja2020.components.general.card-element', ['title' => '#1 ' . ctrans('texts.amount_cents')])
<input type="text" name="transactions[]" class="w-full input" required data-cy="verification-1st">
@endcomponent
@component('portal.ninja2020.components.general.card-element', ['title' => '#2 ' . ctrans('texts.amount_cents')])
<input type="text" name="transactions[]" class="w-full input" required data-cy="verification-2nd">
@endcomponent
@component('portal.ninja2020.gateways.includes.pay_now', ['type' => 'submit'])
{{ ctrans('texts.complete_verification')}}
@endcomponent
</form>
@endsection

View File

@ -0,0 +1,57 @@
@extends('portal.ninja2020.layout.payments', ['gateway_title' => 'ACH', 'card_title' => 'ACH'])
@section('gateway_content')
@if(count($tokens) > 0)
<div class="alert alert-failure mb-4" hidden id="errors"></div>
@include('portal.ninja2020.gateways.includes.payment_details')
<form action="{{ route('client.payments.response') }}" method="post" id="server-response">
@csrf
<input type="hidden" name="company_gateway_id" value="{{ $gateway->getCompanyGatewayId() }}">
<input type="hidden" name="payment_method_id" value="{{ $payment_method_id }}">
<input type="hidden" name="amount" value="{{ $amount }}">
<input type="hidden" name="currency" value="{{ $currency }}">
<input type="hidden" name="payment_hash" value="{{ $payment_hash }}">
<input type="hidden" name="source" value="">
</form>
@component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.pay_with')])
@if(count($tokens) > 0)
@foreach($tokens as $token)
<label class="mr-4">
<input
type="radio"
data-token="{{ $token->hashed_id }}"
name="payment-type"
class="form-radio cursor-pointer toggle-payment-with-token"/>
<span class="ml-1 cursor-pointer">{{ ctrans('texts.bank_transfer') }} (*{{ $token->meta->last4 }})</span>
</label>
@endforeach
@endisset
@endcomponent
@else
@component('portal.ninja2020.components.general.card-element-single', ['title' => 'ACH', 'show_title' => false])
<span>{{ ctrans('texts.bank_account_not_linked') }}</span>
<a class="button button-link text-primary"
href="{{ route('client.payment_methods.index') }}">{{ ctrans('texts.add_payment_method') }}</a>
@endcomponent
@endif
@include('portal.ninja2020.gateways.includes.pay_now')
@endsection
@push('footer')
<script>
Array
.from(document.getElementsByClassName('toggle-payment-with-token'))
.forEach((element) => element.addEventListener('click', (element) => {
document.querySelector('input[name=source]').value = element.target.dataset.token;
}));
document.getElementById('pay-now').addEventListener('click', function () {
document.getElementById('server-response').submit();
});
</script>
@endpush

View File

@ -0,0 +1,249 @@
@extends('portal.ninja2020.layout.payments', ['gateway_title' => ctrans('texts.credit_card'), 'card_title' => ctrans('texts.credit_card')])
@section('gateway_head')
<script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
<script src="{{ asset('js/clients/payments/card-js.min.js') }}"></script>
<link href="{{ asset('css/card-js.min.css') }}" rel="stylesheet" type="text/css">
@endsection
@section('gateway_content')
<form action="{{ route('client.payments.response') }}" method="post" id="server-response">
@csrf
<input type="hidden" name="gateway_response">
<input type="hidden" name="store_card" id="store_card">
<input type="hidden" name="payment_hash" value="{{ $payment_hash }}">
<input type="hidden" name="company_gateway_id" value="{{ $gateway->getCompanyGatewayId() }}">
<input type="hidden" name="payment_method_id" value="1">
<input type="hidden" name="token" id="token" value="">
<input type="hidden" name="credit_card_id" id="credit_card_id" value="">
</form>
<div class="alert alert-failure mb-4" hidden id="errors"></div>
@component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.payment_type')])
{{ ctrans('texts.credit_card') }}
@endcomponent
@include('portal.ninja2020.gateways.includes.payment_details')
@component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.pay_with')])
@if(count($tokens) > 0)
@foreach($tokens as $token)
<label class="mr-4">
<input
type="radio"
data-token="{{ $token->token }}"
name="payment-type"
class="form-radio cursor-pointer toggle-payment-with-token"/>
<span class="ml-1 cursor-pointer">**** {{ optional($token->meta)->last4 }}</span>
</label>
@endforeach
@endisset
<label>
<input
type="radio"
id="toggle-payment-with-credit-card"
class="form-radio cursor-pointer"
name="payment-type"
checked/>
<span class="ml-1 cursor-pointer">{{ __('texts.new_card') }}</span>
</label>
@endcomponent
@include('portal.ninja2020.gateways.includes.save_card')
@include('portal.ninja2020.gateways.wepay.includes.credit_card')
@include('portal.ninja2020.gateways.includes.pay_now')
@endsection
@section('gateway_footer')
<script type="text/javascript" src="https://static.wepay.com/min/js/tokenization.4.latest.js"></script>
<script type="text/javascript">
(function() {
@if(config('ninja.wepay.environment') == 'staging')
WePay.set_endpoint("stage");
@else
WePay.set_endpoint("production");
@endif
// Shortcuts
var d = document;
d.id = d.getElementById,
valueById = function(id) {
return d.id(id).value;
};
// For those not using DOM libraries
var addEvent = function(e,v,f) {
if (!!window.attachEvent) { e.attachEvent('on' + v, f); }
else { e.addEventListener(v, f, false); }
};
let errors = document.getElementById('errors');
/* handle the switch between token and cc */
Array
.from(document.getElementsByClassName('toggle-payment-with-token'))
.forEach((element) => element.addEventListener('click', (e) => {
document
.getElementById('save-card--container').style.display = 'none';
document
.getElementById('wepay--credit-card-container').style.display = 'none';
document
.getElementById('token').value = e.target.dataset.token;
}));
let payWithCreditCardToggle = document.getElementById('toggle-payment-with-credit-card');
if (payWithCreditCardToggle) {
payWithCreditCardToggle
.addEventListener('click', () => {
document
.getElementById('save-card--container').style.display = 'grid';
document
.getElementById('wepay--credit-card-container').style.display = 'flex';
document
.getElementById('token').value = null;
});
}
/* handle the switch between token and cc */
/* Pay Now Button */
let payNowButton = document.getElementById('pay-now');
if (payNowButton) {
payNowButton
.addEventListener('click', (e) => {
payNowButton.disabled = true;
payNowButton.querySelector('svg').classList.remove('hidden');
payNowButton.querySelector('span').classList.add('hidden');
let token = document.getElementById('token').value;
/* Attach store card value to form */
let storeCard = document.querySelector('input[name=token-billing-checkbox]:checked');
if (storeCard) {
document.getElementById("store_card").value = storeCard.value;
}
/* Attach store card value to form */
if(token){
handleTokenPayment(token)
}
else{
handleCardPayment();
}
});
}
/* Pay Now Button */
function handleTokenPayment(token)
{
document.querySelector('input[name="credit_card_id"]').value = null;
document.querySelector('input[name="token"]').value = token;
document.getElementById('server-response').submit();
}
// Attach the event to the DOM
function handleCardPayment(){
var myCard = $('#my-card');
let payNowButton = document.getElementById('pay-now');
if(document.getElementById('cardholder_name') == "") {
document.getElementById('cardholder_name').focus();
errors.textContent = "Cardholder name required.";
errors.hidden = false;
return;
}
else if(myCard.CardJs('cardNumber') == ""){
document.getElementById('card_number').focus();
errors.textContent = "Card number required.";
errors.hidden = false;
return;
}
else if(myCard.CardJs('cvc') == ""){
document.getElementById('cvv').focus();
errors.textContent = "CVV number required.";
errors.hidden = false;
return;
}
else if(myCard.CardJs('expiryMonth') == ""){
// document.getElementById('expiry_month').focus();
errors.textContent = "Expiry Month number required.";
errors.hidden = false;
return;
}
else if(myCard.CardJs('expiryYear') == ""){
// document.getElementById('expiry_year').focus();
errors.textContent = "Expiry Year number required.";
errors.hidden = false;
return;
}
var userName = [valueById('cardholder_name')].join(' ');
response = WePay.credit_card.create({
"client_id": "{{ config('ninja.wepay.client_id') }}",
"user_name": valueById('cardholder_name'),
"email": "{{ $contact->email }}",
"cc_number": myCard.CardJs('cardNumber'),
"cvv": myCard.CardJs('cvc'),
"expiration_month": myCard.CardJs('expiryMonth'),
"expiration_year": myCard.CardJs('expiryYear'),
"address": {
"postal_code": "{{ $contact->client->postal_code }}"
}
}, function(data) {
if (data.error) {
//console.log(data);
// handle error response error_description
payNowButton.disabled = false;
payNowButton.querySelector('svg').classList.add('hidden');
payNowButton.querySelector('span').classList.remove('hidden');
errors.textContent = '';
errors.textContent = data.error_description;
errors.hidden = false;
} else {
var token = data.credit_card_id;
document.querySelector('input[name="credit_card_id"]').value = token;
document.querySelector('input[name="token"]').value = null;
document.getElementById('server-response').submit();
}
});
}
})();
</script>
@endsection

View File

@ -0,0 +1,12 @@
<div class="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6"
style="display: flex!important; justify-content: center!important;" id="wepay--credit-card-container">
<div class="card-js" id="my-card" data-capture-name="true">
<input class="name" id="cardholder_name" name="card-holders-name" placeholder="{{ ctrans('texts.name')}}" required>
<input class="card-number my-custom-class" id="card_number" name="card-number" required>
<input class="expiry-month" name="expiry-month" id="expiration_month" required>
<input class="expiry-year" name="expiry-year" id="expiration_year" required>
<input class="cvc" name="cvc" id="cvv" required>
</div>
<div id="errors"></div>
</div>

View File

@ -0,0 +1,24 @@
@extends('portal.ninja2020.layout.clean', ['custom_body_class' => 'bg-gray-50'])
@section('meta_title', ctrans('texts.sign_up_with_wepay'))
@section('body')
<div class="flex flex-col justify-center items-center mt-10">
<img src="{{ asset('images/wepay.svg') }}" alt="We Pay">
</div>
<div class="flex flex-col justify-center items-center mt-10">
<h1>Wepay setup complete:</h1>
</div>
<div class="flex flex-col justify-center items-center mt-10">
@if(isset($message))
{{ $message ?? '' }}
@endif
</div>
</div>
@endsection
@push('footer')
<script>
</script>
@endpush

View File

@ -0,0 +1,15 @@
@extends('portal.ninja2020.layout.clean', ['custom_body_class' => 'bg-gray-50'])
@section('meta_title', ctrans('texts.sign_up_with_wepay'))
@section('body')
<div class="flex flex-col justify-center items-center mt-10">
<img src="{{ asset('images/wepay.svg') }}" alt="We Pay">
</div>
@livewire('wepay-signup', ['user_id' => $user_id, 'company' => $company])
@endsection
@push('footer')
<script>
</script>
@endpush

View File

@ -0,0 +1,285 @@
<div class="flex flex-col justify-center items-center my-10">
<form wire:submit.prevent="submit">
@csrf
@method('POST')
<div class="shadow overflow-hidden rounded">
<div class="px-4 py-5 bg-white sm:p-6">
<div class="grid grid-cols-6 gap-6 max-w-4xl">
<div class="col-span-6 sm:col-span-3">
<label for="first_name" class="input-label">@lang('texts.first_name')</label>
<input id="first_name" class="input w-full" name="first_name" wire:model.defer="first_name"/>
@error('first_name')
<div class="validation validation-fail">
{{ $message }}
</div>
@enderror
</div>
<div class="col-span-6 sm:col-span-3">
<label for="last_name" class="input-label">@lang('texts.last_name')</label>
<input id="last_name" class="input w-full" name="last_name" wire:model.defer="last_name"/>
@error('last_name')
<div class="validation validation-fail">
{{ $message }}
</div>
@enderror
</div>
<div class="col-span-6 sm:col-span-4">
<label for="email_address" class="input-label">@lang('texts.email_address')</label>
<input id="email_address" class="input w-full" type="email" name="email"
wire:model.defer="email" disabled="true"/>
@error('email')
<div class="validation validation-fail">
{{ $message }}
</div>
@enderror
</div>
<div class="col-span-6 sm:col-span-4">
<label for="company_name" class="input-label">@lang('texts.company_name')</label>
<input id="company_name" class="input w-full" name="company_name"
wire:model.defer="company_name"/>
@error('company_name')
<div class="validation validation-fail">
{{ $message }}
</div>
@enderror
</div>
<div class="col-span-6 sm:col-span-4 flex items-center">
<label for="country" class="input-label mr-4">@lang('texts.country')</label>
<div class="radio mr-4">
<input class="form-radio cursor-pointer" type="radio" value="US" name="country" checked
wire:model.defer="country">
<span>{{ ctrans('texts.country_United States') }}</span>
</div>
<div class="radio mr-4">
<input class="form-radio cursor-pointer" type="radio" value="CA" name="country"
wire:model.defer="country">
<span>{{ ctrans('texts.country_Canada') }}</span>
</div>
<div class="radio mr-4">
<input class="form-radio cursor-pointer" type="radio" value="GB" name="country"
wire:model.defer="country">
<span>{{ ctrans('texts.country_United Kingdom') }}</span>
</div>
</div>
@if($country == 'CA')
<div class="col-span-6 sm:col-span-4 {{ $country != 'CA' ? 'hidden' : 'block' }}">
<label for="country" class="input-label">@lang('texts.debit_cards')</label>
<div class="checkbox">
<input class="form-checkbox cursor-pointer mr-2" type="checkbox" name="debit_cards" value="1" wire:model.defer="debit_cards">
<span>{{ ctrans('texts.accept_debit_cards') }}</span>
</div>
</div>
@endif
@if($country == 'US')
<div class="col-span-6 sm:col-span-4 {{ $country != 'US' ? 'hidden' : 'block' }}">
<label for="country" class="input-label">@lang('texts.ach')</label>
<div class="checkbox">
<input class="form-checkbox cursor-pointer mr-2" type="checkbox" name="ach" value="1" wire:model="ach">
<span>{{ ctrans('texts.enable_ach')}}</span>
</div>
</div>
@endif
<div class="col-span-6 sm:col-span-4">
<label for="country" class="input-label"></label>
<div class="checkbox">
<input class="form-checkbox cursor-pointer mr-2" type="checkbox" name="wepay_payment_tos_agree" value="1" wire:model.defer="wepay_payment_tos_agree">
<span>{!! ctrans('texts.wepay_payment_tos_agree', ['terms' => $terms, 'privacy_policy' => $privacy_policy]) !!}</span>
</div>
@error('wepay_payment_tos_agree')
<div class="validation validation-fail">
{{ $message }}
</div>
@enderror
</div>
<div class="col-span-6 sm:col-span-4">
<span><i>{{ ctrans('texts.standard_fees_apply')}}</i></span>
</div>
<div class="col-span-6 {{ $country != 'CA' ? 'hidden' : 'block' }}">
<table id="canadaFees" width="100%" class="min-w-full"
style="border: 1px solid black; margin-bottom: 40px; display: table;">
<tbody>
<tr style="border: solid 1px black">
<th colspan="2" style="text-align:center;padding: 4px">
Fees Disclosure Box
</th>
</tr>
<tr style="border: solid 1px black;vertical-align:top">
<td style="border-left: solid 1px black; padding: 8px">
<h4>Payment Card Type</h4>
(These are the most common domestically issued card types
and processing methods. They do not represent all the
possible fees and variations that are charged to the
merchants.)
</td>
<td style="padding: 8px">
<h4>Processing Method: Card Not Present</h4>
(Means that the card/device was not
electronically read. Generally, the card
information is manually key-entered, e.g. online
payment)
</td>
</tr>
<tr>
<td style="border-left: solid 1px black;padding-left:8px;padding-top:4px;">
Visa Consumer Credit
</td>
<td style="text-align:center">
2.9% + CA$0.30
</td>
</tr>
<tr>
<td style="border-left: solid 1px black;padding-left:8px;padding-top:4px;">
Visa Infinite
</td>
<td style="text-align:center">
2.9% + CA$0.30
</td>
</tr>
<tr>
<td style="border-left: solid 1px black;padding-left:8px;padding-top:4px;">
Visa Infinite Privilege
</td>
<td style="text-align:center">
2.9% + CA$0.30
</td>
</tr>
<tr>
<td style="border-left: solid 1px black;padding-left:8px;padding-top:4px;">
Visa Business
</td>
<td style="text-align:center">
2.9% + CA$0.30
</td>
</tr>
<tr>
<td style="border-left: solid 1px black;padding-left:8px;padding-top:4px;">
Visa Business Premium
</td>
<td style="text-align:center">
2.9% + CA$0.30
</td>
</tr>
<tr>
<td style="border-left: solid 1px black;padding-left:8px;padding-top:4px;">
Visa Corporate
</td>
<td style="text-align:center">
2.9% + CA$0.30
</td>
</tr>
<tr>
<td style="border-left: solid 1px black;padding-left:8px;padding-top:4px;">
Visa Prepaid
</td>
<td style="text-align:center">
2.9% + CA$0.30
</td>
</tr>
<tr>
<td style="border-left: solid 1px black;padding-left:8px;padding-top:4px;">
Visa Debit
</td>
<td style="text-align:center">
2.9% + CA$0.30
</td>
</tr>
<tr>
<td style="border-left: solid 1px black;padding-left:8px;padding-top:4px;">
MasterCard Consumer Credit
</td>
<td style="text-align:center">
2.9% + CA$0.30
</td>
</tr>
<tr>
<td style="border-left: solid 1px black;padding-left:8px;padding-top:4px;">
MasterCard World
</td>
<td style="text-align:center">
2.9% + CA$0.30
</td>
</tr>
<tr>
<td style="border-left: solid 1px black;padding-left:8px;padding-top:4px;">
MasterCard World Elite
</td>
<td style="text-align:center">
2.9% + CA$0.30
</td>
</tr>
<tr>
<td style="border-left: solid 1px black;padding-left:8px;padding-top:4px;">
MasterCard Business/Corporate
</td>
<td style="text-align:center">
2.9% + CA$0.30
</td>
</tr>
<tr>
<td style="border-left: solid 1px black;padding-left:8px;padding-top:4px;">
MasterCard Debit
</td>
<td style="text-align:center">
2.9% + CA$0.30
</td>
</tr>
<tr>
<td style="border-left: solid 1px black;padding-left:8px;padding-top:4px;">
MasterCard Prepaid
</td>
<td style="text-align:center">
2.9% + CA$0.30
</td>
</tr>
<tr>
<td style="border-left: solid 1px black;padding-left:8px;padding-top:4px;">
American Express
</td>
<td style="text-align:center">
2.9% + CA$0.30
</td>
</tr>
<tr style="border: solid 1px black;">
<th colspan="2" style="text-align:center;padding: 4px">
Other Fees Disclosure Box
</th>
</tr>
<tr style="border: solid 1px black;">
<td style="border-left: solid 1px black;padding-left:8px;padding-top:4px;">
Chargeback
</td>
<td style="text-align:center">
CA$15.00
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="px-4 py-3 bg-gray-50 text-right sm:px-6">
<button class="button button-primary bg-primary">{{ $saved }}</button>
</div>
</div>
</form>
</div>

View File

@ -80,6 +80,7 @@ Route::group(['middleware' => ['auth:contact', 'locale', 'check_client_existence
Route::post('upload', 'ClientPortal\UploadController')->name('upload.store');
Route::get('logout', 'Auth\ContactLoginController@logout')->name('logout');
});
Route::get('client/subscriptions/{subscription}/purchase', 'ClientPortal\SubscriptionPurchaseController@index')->name('client.subscription.purchase')->middleware('domain_db');

View File

@ -20,6 +20,9 @@ Route::post('password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail'
Route::get('password/reset/{token}', 'Auth\ResetPasswordController@showResetForm')->middleware(['domain_db','email_db'])->name('password.reset');
Route::post('password/reset', 'Auth\ResetPasswordController@reset')->middleware('email_db')->name('password.update');
Route::get('wepay/signup/{token}', 'WePayController@signup')->name('wepay.signup');
Route::get('wepay/finished', 'WePayController@finished')->name('wepay.finished');
/*
* Social authentication
*/