From cbe2b2905e679a4b1b41d566f7b7cec6f4e9d7bc Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Mon, 18 Jan 2016 10:30:42 +0200 Subject: [PATCH] Added autolink to emails and fixed checkout.com integration --- app/Http/Controllers/InvoiceController.php | 116 ------ app/Http/Controllers/PaymentController.php | 3 +- .../Controllers/PublicClientController.php | 139 +++++++- app/Libraries/Utils.php | 5 + app/Libraries/lib_autolink.php | 335 ++++++++++++++++++ app/Ninja/Mailers/ContactMailer.php | 3 +- app/Providers/AppServiceProvider.php | 2 +- app/Services/PaymentService.php | 24 ++ composer.json | 5 +- database/seeds/PaymentLibrariesSeeder.php | 2 +- readme.md | 2 - .../emails/partials/account_logo.blade.php | 2 +- resources/views/invoices/view.blade.php | 40 ++- .../partials/checkout_com_payment.blade.php | 22 ++ tests/acceptance/TaxRatesCest.php | 103 ++++++ 15 files changed, 660 insertions(+), 143 deletions(-) create mode 100644 app/Libraries/lib_autolink.php create mode 100644 resources/views/partials/checkout_com_payment.blade.php create mode 100644 tests/acceptance/TaxRatesCest.php diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php index 6fa1d1ae5600..83714effe168 100644 --- a/app/Http/Controllers/InvoiceController.php +++ b/app/Http/Controllers/InvoiceController.php @@ -20,12 +20,9 @@ use App\Models\Product; use App\Models\TaxRate; use App\Models\InvoiceDesign; use App\Models\Activity; -use App\Models\Gateway; use App\Ninja\Mailers\ContactMailer as Mailer; use App\Ninja\Repositories\InvoiceRepository; use App\Ninja\Repositories\ClientRepository; -use App\Events\InvoiceInvitationWasViewed; -use App\Events\QuoteInvitationWasViewed; use App\Services\InvoiceService; use App\Services\RecurringInvoiceService; use App\Http\Requests\SaveInvoiceWithClientRequest; @@ -86,119 +83,6 @@ class InvoiceController extends BaseController return $this->recurringInvoiceService->getDatatable($accountId, $clientPublicId, ENTITY_RECURRING_INVOICE, $search); } - public function view($invitationKey) - { - if (!$invitation = $this->invoiceRepo->findInvoiceByInvitation($invitationKey)) { - return response()->view('error', [ - 'error' => trans('texts.invoice_not_found'), - 'hideHeader' => true, - ]); - } - - $invoice = $invitation->invoice; - $client = $invoice->client; - $account = $invoice->account; - - if (!$account->checkSubdomain(Request::server('HTTP_HOST'))) { - return response()->view('error', [ - 'error' => trans('texts.invoice_not_found'), - 'hideHeader' => true, - 'clientViewCSS' => $account->clientViewCSS(), - 'clientFontUrl' => $account->getFontsUrl(), - ]); - } - - if (!Input::has('phantomjs') && !Session::has($invitationKey) && (!Auth::check() || Auth::user()->account_id != $invoice->account_id)) { - if ($invoice->is_quote) { - event(new QuoteInvitationWasViewed($invoice, $invitation)); - } else { - event(new InvoiceInvitationWasViewed($invoice, $invitation)); - } - } - - Session::put($invitationKey, true); // track this invitation has been seen - Session::put('invitation_key', $invitationKey); // track current invitation - - $account->loadLocalizationSettings($client); - - $invoice->invoice_date = Utils::fromSqlDate($invoice->invoice_date); - $invoice->due_date = Utils::fromSqlDate($invoice->due_date); - $invoice->is_pro = $account->isPro(); - $invoice->invoice_fonts = $account->getFontsData(); - - if ($invoice->invoice_design_id == CUSTOM_DESIGN) { - $invoice->invoice_design->javascript = $account->custom_design; - } else { - $invoice->invoice_design->javascript = $invoice->invoice_design->pdfmake; - } - $contact = $invitation->contact; $contact->setVisible([ - 'first_name', - 'last_name', - 'email', - 'phone', - ]); - - $paymentTypes = $this->getPaymentTypes($client, $invitation); - $paymentURL = ''; - if (count($paymentTypes)) { - $paymentURL = $paymentTypes[0]['url']; - if (!$account->isGatewayConfigured(GATEWAY_PAYPAL_EXPRESS)) { - $paymentURL = URL::to($paymentURL); - } - } - - $showApprove = $invoice->quote_invoice_id ? false : true; - if ($invoice->due_date) { - $showApprove = time() < strtotime($invoice->due_date); - } - - $data = array( - 'showApprove' => $showApprove, - 'showBreadcrumbs' => false, - 'hideLogo' => $account->isWhiteLabel(), - 'hideHeader' => $account->isNinjaAccount(), - 'clientViewCSS' => $account->clientViewCSS(), - 'clientFontUrl' => $account->getFontsUrl(), - 'invoice' => $invoice->hidePrivateFields(), - 'invitation' => $invitation, - 'invoiceLabels' => $account->getInvoiceLabels(), - 'contact' => $contact, - 'paymentTypes' => $paymentTypes, - 'paymentURL' => $paymentURL, - 'phantomjs' => Input::has('phantomjs'), - ); - - return View::make('invoices.view', $data); - } - - private function getPaymentTypes($client, $invitation) - { - $paymentTypes = []; - $account = $client->account; - - if ($client->getGatewayToken()) { - $paymentTypes[] = [ - 'url' => URL::to("payment/{$invitation->invitation_key}/token"), 'label' => trans('texts.use_card_on_file') - ]; - } - foreach(Gateway::$paymentTypes as $type) { - if ($account->getGatewayByType($type)) { - $typeLink = strtolower(str_replace('PAYMENT_TYPE_', '', $type)); - $url = URL::to("/payment/{$invitation->invitation_key}/{$typeLink}"); - - // PayPal doesn't allow being run in an iframe so we need to open in new tab - if ($type === PAYMENT_TYPE_PAYPAL && $account->iframe_url) { - $url = 'javascript:window.open("'.$url.'", "_blank")'; - } - $paymentTypes[] = [ - 'url' => $url, 'label' => trans('texts.'.strtolower($type)) - ]; - } - } - - return $paymentTypes; - } - public function edit($publicId, $clone = false) { $account = Auth::user()->account; diff --git a/app/Http/Controllers/PaymentController.php b/app/Http/Controllers/PaymentController.php index 1b055969138f..681956dd2a03 100644 --- a/app/Http/Controllers/PaymentController.php +++ b/app/Http/Controllers/PaymentController.php @@ -532,7 +532,8 @@ class PaymentController extends BaseController try { if (method_exists($gateway, 'completePurchase') - && !$accountGateway->isGateway(GATEWAY_TWO_CHECKOUT)) { + && !$accountGateway->isGateway(GATEWAY_TWO_CHECKOUT) + && !$accountGateway->isGateway(GATEWAY_CHECKOUT_COM)) { $details = $this->paymentService->getPaymentDetails($invitation, $accountGateway); $response = $this->paymentService->completePurchase($gateway, $accountGateway, $details, $token); $ref = $response->getTransactionReference() ?: $token; diff --git a/app/Http/Controllers/PublicClientController.php b/app/Http/Controllers/PublicClientController.php index 5dea7e9d8c9c..aabd2fdf1652 100644 --- a/app/Http/Controllers/PublicClientController.php +++ b/app/Http/Controllers/PublicClientController.php @@ -1,25 +1,162 @@ invoiceRepo = $invoiceRepo; $this->paymentRepo = $paymentRepo; $this->activityRepo = $activityRepo; + $this->paymentService = $paymentService; + } + + public function view($invitationKey) + { + if (!$invitation = $this->invoiceRepo->findInvoiceByInvitation($invitationKey)) { + return response()->view('error', [ + 'error' => trans('texts.invoice_not_found'), + 'hideHeader' => true, + ]); + } + + $invoice = $invitation->invoice; + $client = $invoice->client; + $account = $invoice->account; + + if (!$account->checkSubdomain(Request::server('HTTP_HOST'))) { + return response()->view('error', [ + 'error' => trans('texts.invoice_not_found'), + 'hideHeader' => true, + 'clientViewCSS' => $account->clientViewCSS(), + 'clientFontUrl' => $account->getFontsUrl(), + ]); + } + + if (!Input::has('phantomjs') && !Session::has($invitationKey) && (!Auth::check() || Auth::user()->account_id != $invoice->account_id)) { + if ($invoice->is_quote) { + event(new QuoteInvitationWasViewed($invoice, $invitation)); + } else { + event(new InvoiceInvitationWasViewed($invoice, $invitation)); + } + } + + Session::put($invitationKey, true); // track this invitation has been seen + Session::put('invitation_key', $invitationKey); // track current invitation + + $account->loadLocalizationSettings($client); + + $invoice->invoice_date = Utils::fromSqlDate($invoice->invoice_date); + $invoice->due_date = Utils::fromSqlDate($invoice->due_date); + $invoice->is_pro = $account->isPro(); + $invoice->invoice_fonts = $account->getFontsData(); + + if ($invoice->invoice_design_id == CUSTOM_DESIGN) { + $invoice->invoice_design->javascript = $account->custom_design; + } else { + $invoice->invoice_design->javascript = $invoice->invoice_design->pdfmake; + } + $contact = $invitation->contact; + $contact->setVisible([ + 'first_name', + 'last_name', + 'email', + 'phone', + ]); + + $paymentTypes = $this->getPaymentTypes($client, $invitation); + $paymentURL = ''; + if (count($paymentTypes)) { + $paymentURL = $paymentTypes[0]['url']; + if (!$account->isGatewayConfigured(GATEWAY_PAYPAL_EXPRESS)) { + $paymentURL = URL::to($paymentURL); + } + } + + $showApprove = $invoice->quote_invoice_id ? false : true; + if ($invoice->due_date) { + $showApprove = time() < strtotime($invoice->due_date); + } + + // Checkout.com requires first getting a payment token + $checkoutComToken = false; + $checkoutComKey = false; + if ($accountGateway = $account->getGatewayConfig(GATEWAY_CHECKOUT_COM)) { + if ($checkoutComToken = $this->paymentService->getCheckoutComToken($invitation)) { + $checkoutComKey = $accountGateway->getConfigField('publicApiKey'); + $invitation->transaction_reference = $checkoutComToken; + $invitation->save(); + } + } + + $data = array( + 'account' => $account, + 'showApprove' => $showApprove, + 'showBreadcrumbs' => false, + 'hideLogo' => $account->isWhiteLabel(), + 'hideHeader' => $account->isNinjaAccount(), + 'clientViewCSS' => $account->clientViewCSS(), + 'clientFontUrl' => $account->getFontsUrl(), + 'invoice' => $invoice->hidePrivateFields(), + 'invitation' => $invitation, + 'invoiceLabels' => $account->getInvoiceLabels(), + 'contact' => $contact, + 'paymentTypes' => $paymentTypes, + 'paymentURL' => $paymentURL, + 'checkoutComToken' => $checkoutComToken, + 'checkoutComKey' => $checkoutComKey, + 'phantomjs' => Input::has('phantomjs'), + ); + + return View::make('invoices.view', $data); + } + + private function getPaymentTypes($client, $invitation) + { + $paymentTypes = []; + $account = $client->account; + + if ($client->getGatewayToken()) { + $paymentTypes[] = [ + 'url' => URL::to("payment/{$invitation->invitation_key}/token"), 'label' => trans('texts.use_card_on_file') + ]; + } + foreach(Gateway::$paymentTypes as $type) { + if ($account->getGatewayByType($type)) { + $typeLink = strtolower(str_replace('PAYMENT_TYPE_', '', $type)); + $url = URL::to("/payment/{$invitation->invitation_key}/{$typeLink}"); + + // PayPal doesn't allow being run in an iframe so we need to open in new tab + if ($type === PAYMENT_TYPE_PAYPAL && $account->iframe_url) { + $url = 'javascript:window.open("'.$url.'", "_blank")'; + } + $paymentTypes[] = [ + 'url' => $url, 'label' => trans('texts.'.strtolower($type)) + ]; + } + } + + return $paymentTypes; } public function dashboard() diff --git a/app/Libraries/Utils.php b/app/Libraries/Utils.php index e60feff84e99..d86ff218ca70 100644 --- a/app/Libraries/Utils.php +++ b/app/Libraries/Utils.php @@ -46,6 +46,11 @@ class Utils return file_exists(storage_path() . '/framework/down'); } + public static function isCron() + { + return php_sapi_name() == 'cli'; + } + public static function isNinja() { return self::isNinjaProd() || self::isNinjaDev(); diff --git a/app/Libraries/lib_autolink.php b/app/Libraries/lib_autolink.php new file mode 100644 index 000000000000..3bc86b843e29 --- /dev/null +++ b/app/Libraries/lib_autolink.php @@ -0,0 +1,335 @@ + + # This code is licensed under the MIT license + # + + #################################################################### + + # + # These are global options. You can set them before calling the autolinking + # functions to change the output. + # + + $GLOBALS['autolink_options'] = array( + + # Should http:// be visibly stripped from the front + # of URLs? + 'strip_protocols' => false, + + ); + + #################################################################### + + function autolink($text, $limit=30, $tagfill='', $auto_title = true){ + + $text = autolink_do($text, '![a-z][a-z-]+://!i', $limit, $tagfill, $auto_title); + $text = autolink_do($text, '!(mailto|skype):!i', $limit, $tagfill, $auto_title); + $text = autolink_do($text, '!www\\.!i', $limit, $tagfill, $auto_title, 'http://'); + return $text; + } + + #################################################################### + + function autolink_do($text, $sub, $limit, $tagfill, $auto_title, $force_prefix=null){ + + $text_l = StrToLower($text); + $cursor = 0; + $loop = 1; + $buffer = ''; + + while (($cursor < strlen($text)) && $loop){ + + $ok = 1; + $matched = preg_match($sub, $text_l, $m, PREG_OFFSET_CAPTURE, $cursor); + + if (!$matched){ + + $loop = 0; + $ok = 0; + + }else{ + + $pos = $m[0][1]; + $sub_len = strlen($m[0][0]); + + $pre_hit = substr($text, $cursor, $pos-$cursor); + $hit = substr($text, $pos, $sub_len); + $pre = substr($text, 0, $pos); + $post = substr($text, $pos + $sub_len); + + $fail_text = $pre_hit.$hit; + $fail_len = strlen($fail_text); + + # + # substring found - first check to see if we're inside a link tag already... + # + + $bits = preg_split("!!i", $pre); + $last_bit = array_pop($bits); + if (preg_match("!\n"; + + $ok = 0; + $cursor += $fail_len; + $buffer .= $fail_text; + } + } + + # + # looks like a nice spot to autolink from - check the pre + # to see if there was whitespace before this match + # + + if ($ok){ + + if ($pre){ + if (!preg_match('![\s\(\[\{>]$!s', $pre)){ + + #echo "fail 2 at $cursor ($pre)
\n"; + + $ok = 0; + $cursor += $fail_len; + $buffer .= $fail_text; + } + } + } + + # + # we want to autolink here - find the extent of the url + # + + if ($ok){ + if (preg_match('/^([a-z0-9\-\.\/\-_%~!?=,:;&+*#@\(\)\$]+)/i', $post, $matches)){ + + $url = $hit.$matches[1]; + + $cursor += strlen($url) + strlen($pre_hit); + $buffer .= $pre_hit; + + $url = html_entity_decode($url); + + + # + # remove trailing punctuation from url + # + + while (preg_match('|[.,!;:?]$|', $url)){ + $url = substr($url, 0, strlen($url)-1); + $cursor--; + } + foreach (array('()', '[]', '{}') as $pair){ + $o = substr($pair, 0, 1); + $c = substr($pair, 1, 1); + if (preg_match("!^(\\$c|^)[^\\$o]+\\$c$!", $url)){ + $url = substr($url, 0, strlen($url)-1); + $cursor--; + } + } + + + # + # nice-i-fy url here + # + + $link_url = $url; + $display_url = $url; + + if ($force_prefix) $link_url = $force_prefix.$link_url; + + if ($GLOBALS['autolink_options']['strip_protocols']){ + if (preg_match('!^(http|https)://!i', $display_url, $m)){ + + $display_url = substr($display_url, strlen($m[1])+3); + } + } + + $display_url = autolink_label($display_url, $limit); + + + # + # add the url + # + + $currentTagfill = $tagfill; + if ($display_url != $link_url && !preg_match('@title=@msi',$currentTagfill) && $auto_title) { + + $display_quoted = preg_quote($display_url, '!'); + + if (!preg_match("!^(http|https)://{$display_quoted}$!i", $link_url)){ + + $currentTagfill .= ' title="'.$link_url.'"'; + } + } + + $link_url_enc = HtmlSpecialChars($link_url); + $display_url_enc = HtmlSpecialChars($display_url); + + $buffer .= "{$display_url_enc}"; + + }else{ + #echo "fail 3 at $cursor
\n"; + + $ok = 0; + $cursor += $fail_len; + $buffer .= $fail_text; + } + } + + } + + # + # add everything from the cursor to the end onto the buffer. + # + + $buffer .= substr($text, $cursor); + + return $buffer; + } + + #################################################################### + + function autolink_label($text, $limit){ + + if (!$limit){ return $text; } + + if (strlen($text) > $limit){ + return substr($text, 0, $limit-3).'...'; + } + + return $text; + } + + #################################################################### + + function autolink_email($text, $tagfill=''){ + + $atom = '[^()<>@,;:\\\\".\\[\\]\\x00-\\x20\\x7f]+'; # from RFC822 + + #die($atom); + + $text_l = StrToLower($text); + $cursor = 0; + $loop = 1; + $buffer = ''; + + while(($cursor < strlen($text)) && $loop){ + + # + # find an '@' symbol + # + + $ok = 1; + $pos = strpos($text_l, '@', $cursor); + + if ($pos === false){ + + $loop = 0; + $ok = 0; + + }else{ + + $pre = substr($text, $cursor, $pos-$cursor); + $hit = substr($text, $pos, 1); + $post = substr($text, $pos + 1); + + $fail_text = $pre.$hit; + $fail_len = strlen($fail_text); + + #die("$pre::$hit::$post::$fail_text"); + + # + # substring found - first check to see if we're inside a link tag already... + # + + $bits = preg_split("!!i", $pre); + $last_bit = array_pop($bits); + if (preg_match("!\n"; + + $ok = 0; + $cursor += $fail_len; + $buffer .= $fail_text; + } + } + + # + # check backwards + # + + if ($ok){ + if (preg_match("!($atom(\.$atom)*)\$!", $pre, $matches)){ + + # move matched part of address into $hit + + $len = strlen($matches[1]); + $plen = strlen($pre); + + $hit = substr($pre, $plen-$len).$hit; + $pre = substr($pre, 0, $plen-$len); + + }else{ + + #echo "fail 2 at $cursor ($pre)
\n"; + + $ok = 0; + $cursor += $fail_len; + $buffer .= $fail_text; + } + } + + # + # check forwards + # + + if ($ok){ + if (preg_match("!^($atom(\.$atom)*)!", $post, $matches)){ + + # move matched part of address into $hit + + $len = strlen($matches[1]); + + $hit .= substr($post, 0, $len); + $post = substr($post, $len); + + }else{ + #echo "fail 3 at $cursor ($post)
\n"; + + $ok = 0; + $cursor += $fail_len; + $buffer .= $fail_text; + } + } + + # + # commit + # + + if ($ok) { + + $cursor += strlen($pre) + strlen($hit); + $buffer .= $pre; + $buffer .= "$hit"; + + } + + } + + # + # add everything from the cursor to the end onto the buffer. + # + + $buffer .= substr($text, $cursor); + + return $buffer; + } + + #################################################################### + +?> diff --git a/app/Ninja/Mailers/ContactMailer.php b/app/Ninja/Mailers/ContactMailer.php index 219f8104321d..5e75a81120ad 100644 --- a/app/Ninja/Mailers/ContactMailer.php +++ b/app/Ninja/Mailers/ContactMailer.php @@ -260,7 +260,8 @@ class ContactMailer extends Mailer } $str = str_replace(array_keys($variables), array_values($variables), $template); - + $str = autolink($str, 100); + return $str; } } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index bf67c12498cc..74098a3966de 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -62,7 +62,7 @@ class AppServiceProvider extends ServiceProvider { }); HTML::macro('image_data', function($imagePath) { - return 'data:image/jpeg;base64,' . base64_encode(file_get_contents(public_path().'/'.$imagePath)); + return 'data:image/jpeg;base64,' . base64_encode(file_get_contents($imagePath)); }); HTML::macro('flatButton', function($label, $color) { diff --git a/app/Services/PaymentService.php b/app/Services/PaymentService.php index 90c116502ba2..7a5147b0459e 100644 --- a/app/Services/PaymentService.php +++ b/app/Services/PaymentService.php @@ -184,6 +184,30 @@ class PaymentService extends BaseService return $cardReference; } + public function getCheckoutComToken($invitation) + { + $token = false; + $invoice = $invitation->invoice; + $client = $invoice->client; + $account = $invoice->account; + + $accountGateway = $account->getGatewayConfig(GATEWAY_CHECKOUT_COM); + $gateway = $this->createGateway($accountGateway); + + $response = $gateway->purchase([ + 'amount' => $invoice->getRequestedAmount(), + 'currency' => $client->currency ? $client->currency->code : ($account->currency ? $account->currency->code : 'USD') + ])->send(); + + if ($response->isRedirect()) { + $token = $response->getTransactionReference(); + } + + Session::set($invitation->id . 'payment_type', PAYMENT_TYPE_CREDIT_CARD); + + return $token; + } + public function createPayment($invitation, $accountGateway, $ref, $payerId = null) { $invoice = $invitation->invoice; diff --git a/composer.json b/composer.json index a7417f9deaf3..b5bd763f6cdc 100644 --- a/composer.json +++ b/composer.json @@ -86,7 +86,10 @@ ], "psr-4": { "App\\": "app/" - } + }, + "files": [ + "app/Libraries/lib_autolink.php" + ] }, "autoload-dev": { "classmap": [ diff --git a/database/seeds/PaymentLibrariesSeeder.php b/database/seeds/PaymentLibrariesSeeder.php index 8a3907473d65..e89f5ef21711 100644 --- a/database/seeds/PaymentLibrariesSeeder.php +++ b/database/seeds/PaymentLibrariesSeeder.php @@ -110,7 +110,7 @@ class PaymentLibrariesSeeder extends Seeder ['name' => 'Argentine Peso', 'code' => 'ARS', 'symbol' => '$', 'precision' => '2', 'thousand_separator' => '.', 'decimal_separator' => ','], ['name' => 'Bangladeshi Taka', 'code' => 'BDT', 'symbol' => 'Tk', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'], ['name' => 'United Arab Emirates Dirham', 'code' => 'AED', 'symbol' => 'DH ', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'], - ['name' => 'Hong Kong Dollar', 'code' => 'HKD', 'symbol' => '$', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'], + ['name' => 'Hong Kong Dollar', 'code' => 'HKD', 'symbol' => 'HKD ', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'], ['name' => 'Indonesian Rupiah', 'code' => 'IDR', 'symbol' => 'Rp', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'], ['name' => 'Mexican Peso', 'code' => 'MXN', 'symbol' => '$', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'], ['name' => 'Egyptian Pound', 'code' => 'EGP', 'symbol' => 'E£', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'], diff --git a/readme.md b/readme.md index d710247c5959..ab29279e83e8 100644 --- a/readme.md +++ b/readme.md @@ -5,8 +5,6 @@ # Invoice Ninja ### [https://www.invoiceninja.com](https://www.invoiceninja.com) -We're starting to work on our expenses, vendors and receipts module. If you'd like to help us design it please send us an email to join the discussion. - [![Join the chat at https://gitter.im/hillelcoren/invoice-ninja](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/hillelcoren/invoice-ninja?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) ### Referral Program diff --git a/resources/views/emails/partials/account_logo.blade.php b/resources/views/emails/partials/account_logo.blade.php index c16e142fd5c1..98c418bcc061 100644 --- a/resources/views/emails/partials/account_logo.blade.php +++ b/resources/views/emails/partials/account_logo.blade.php @@ -3,7 +3,7 @@ @endif - + @if ($account->website) diff --git a/resources/views/invoices/view.blade.php b/resources/views/invoices/view.blade.php index 6b27fbf7fe0b..04ad660ae1ba 100644 --- a/resources/views/invoices/view.blade.php +++ b/resources/views/invoices/view.blade.php @@ -21,24 +21,28 @@
-

 

-
- @if ($invoice->is_quote) - {!! Button::normal(trans('texts.download_pdf'))->withAttributes(['onclick' => 'onDownloadClick()'])->large() !!}   - @if ($showApprove) - {!! Button::success(trans('texts.approve'))->asLinkTo(URL::to('/approve/' . $invitation->invitation_key))->large() !!} - @endif - @elseif ($invoice->client->account->isGatewayConfigured() && !$invoice->isPaid() && !$invoice->is_recurring) - {!! Button::normal(trans('texts.download_pdf'))->withAttributes(['onclick' => 'onDownloadClick()'])->large() !!}   - @if (count($paymentTypes) > 1) - {!! DropdownButton::success(trans('texts.pay_now'))->withContents($paymentTypes)->large() !!} - @else - {{ trans('texts.pay_now') }} - @endif - @else - {!! Button::normal('Download PDF')->withAttributes(['onclick' => 'onDownloadClick()'])->large() !!} - @endif -
+ @if ($checkoutComToken) + @include('partials.checkout_com_payment') + @else +

 

+
+ @if ($invoice->is_quote) + {!! Button::normal(trans('texts.download_pdf'))->withAttributes(['onclick' => 'onDownloadClick()'])->large() !!}   + @if ($showApprove) + {!! Button::success(trans('texts.approve'))->asLinkTo(URL::to('/approve/' . $invitation->invitation_key))->large() !!} + @endif + @elseif ($invoice->client->account->isGatewayConfigured() && !$invoice->isPaid() && !$invoice->is_recurring) + {!! Button::normal(trans('texts.download_pdf'))->withAttributes(['onclick' => 'onDownloadClick()'])->large() !!}   + @if (count($paymentTypes) > 1) + {!! DropdownButton::success(trans('texts.pay_now'))->withContents($paymentTypes)->large() !!} + @else + {{ trans('texts.pay_now') }} + @endif + @else + {!! Button::normal('Download PDF')->withAttributes(['onclick' => 'onDownloadClick()'])->large() !!} + @endif +
+ @endif

 

diff --git a/resources/views/partials/checkout_com_payment.blade.php b/resources/views/partials/checkout_com_payment.blade.php new file mode 100644 index 000000000000..9e5e63baae50 --- /dev/null +++ b/resources/views/partials/checkout_com_payment.blade.php @@ -0,0 +1,22 @@ + + +
+ +
\ No newline at end of file diff --git a/tests/acceptance/TaxRatesCest.php b/tests/acceptance/TaxRatesCest.php new file mode 100644 index 000000000000..5793b2f12038 --- /dev/null +++ b/tests/acceptance/TaxRatesCest.php @@ -0,0 +1,103 @@ +/checkIfLogin($I); + + $this->faker = Factory::create(); + } + + public function taxRates(AcceptanceTester $I) + { + + } + + /* + public function onlinePayment(AcceptanceTester $I) + { + $I->wantTo('test an online payment'); + + $clientEmail = $this->faker->safeEmail; + $productKey = $this->faker->text(10); + + // set gateway info + $I->wantTo('create a gateway'); + $I->amOnPage('/settings/online_payments'); + + if (strpos($I->grabFromCurrentUrl(), 'create') !== false) { + $I->fillField(['name' =>'23_apiKey'], Fixtures::get('gateway_key')); + $I->selectOption('#token_billing_type_id', 4); + $I->click('Save'); + $I->see('Successfully created gateway'); + } + + // create client + $I->amOnPage('/clients/create'); + $I->fillField(['name' => 'contacts[0][email]'], $clientEmail); + $I->click('Save'); + $I->see($clientEmail); + + // create product + $I->amOnPage('/products/create'); + $I->fillField(['name' => 'product_key'], $productKey); + $I->fillField(['name' => 'notes'], $this->faker->text(80)); + $I->fillField(['name' => 'cost'], $this->faker->numberBetween(1, 20)); + $I->click('Save'); + $I->wait(1); + $I->see($productKey); + + // create invoice + $I->amOnPage('/invoices/create'); + $I->selectDropdown($I, $clientEmail, '.client_select .dropdown-toggle'); + $I->fillField('table.invoice-table tbody tr:nth-child(1) #product_key', $productKey); + $I->click('Save'); + $I->see($clientEmail); + + // enter payment + $clientId = $I->grabFromDatabase('contacts', 'client_id', ['email' => $clientEmail]); + $invoiceId = $I->grabFromDatabase('invoices', 'id', ['client_id' => $clientId]); + $invitationKey = $I->grabFromDatabase('invitations', 'invitation_key', ['invoice_id' => $invoiceId]); + + $clientSession = $I->haveFriend('client'); + $clientSession->does(function(AcceptanceTester $I) use ($invitationKey) { + $I->amOnPage('/view/' . $invitationKey); + $I->click('Pay Now'); + + $I->fillField(['name' => 'first_name'], $this->faker->firstName); + $I->fillField(['name' => 'last_name'], $this->faker->lastName); + $I->fillField(['name' => 'address1'], $this->faker->streetAddress); + $I->fillField(['name' => 'address2'], $this->faker->streetAddress); + $I->fillField(['name' => 'city'], $this->faker->city); + $I->fillField(['name' => 'state'], $this->faker->state); + $I->fillField(['name' => 'postal_code'], $this->faker->postcode); + $I->selectDropdown($I, 'United States', '.country-select .dropdown-toggle'); + $I->fillField(['name' => 'card_number'], '4242424242424242'); + $I->fillField(['name' => 'cvv'], '1234'); + $I->selectOption('#expiration_month', 12); + $I->selectOption('#expiration_year', date('Y')); + $I->click('.btn-success'); + $I->see('Successfully applied payment'); + }); + + $I->wait(1); + + // create recurring invoice and auto-bill + $I->amOnPage('/recurring_invoices/create'); + $I->selectDropdown($I, $clientEmail, '.client_select .dropdown-toggle'); + $I->fillField('table.invoice-table tbody tr:nth-child(1) #product_key', $productKey); + $I->checkOption('#auto_bill'); + $I->executeJS('preparePdfData(\'email\')'); + $I->wait(2); + $I->see("$0.00"); + + } + */ +} \ No newline at end of file