diff --git a/app/Console/Commands/SendRecurringInvoices.php b/app/Console/Commands/SendRecurringInvoices.php
index 574d324d3842..4934ca417e34 100644
--- a/app/Console/Commands/SendRecurringInvoices.php
+++ b/app/Console/Commands/SendRecurringInvoices.php
@@ -45,33 +45,19 @@ class SendRecurringInvoices extends Command
if (!$recurInvoice->user->confirmed) {
continue;
}
-
+
$recurInvoice->account->loadLocalizationSettings($recurInvoice->client);
$this->info('Processing Invoice '.$recurInvoice->id.' - Should send '.($recurInvoice->shouldSendToday() ? 'YES' : 'NO'));
$invoice = $this->invoiceRepo->createRecurringInvoice($recurInvoice);
if ($invoice && !$invoice->isPaid()) {
- $invoice->account->auto_bill_on_due_date;
-
- $autoBillLater = false;
- if ($invoice->account->auto_bill_on_due_date || $this->paymentService->getClientRequiresDelayedAutoBill($invoice->client)) {
- $autoBillLater = true;
- }
-
- if($autoBillLater) {
- if($paymentMethod = $this->paymentService->getClientDefaultPaymentMethod($invoice->client)) {
- $invoice->autoBillPaymentMethod = $paymentMethod;
- }
- }
-
-
$this->info('Sending Invoice');
$this->mailer->sendInvoice($invoice);
}
}
$delayedAutoBillInvoices = Invoice::with('account.timezone', 'recurring_invoice', 'invoice_items', 'client', 'user')
- ->whereRaw('is_deleted IS FALSE AND deleted_at IS NULL AND is_recurring IS FALSE
+ ->whereRaw('is_deleted IS FALSE AND deleted_at IS NULL AND is_recurring IS FALSE
AND balance > 0 AND due_date = ? AND recurring_invoice_id IS NOT NULL',
array($today->format('Y-m-d')))
->orderBy('invoices.id', 'asc')
@@ -79,17 +65,12 @@ class SendRecurringInvoices extends Command
$this->info(count($delayedAutoBillInvoices).' due recurring invoice instance(s) found');
foreach ($delayedAutoBillInvoices as $invoice) {
- $autoBill = $invoice->getAutoBillEnabled();
- $billNow = false;
-
- if ($autoBill && !$invoice->isPaid()) {
- $billNow = $invoice->account->auto_bill_on_due_date || $this->paymentService->getClientRequiresDelayedAutoBill($invoice->client);
+ if ($invoice->isPaid()) {
+ continue;
}
- $this->info('Processing Invoice '.$invoice->id.' - Should bill '.($billNow ? 'YES' : 'NO'));
-
- if ($billNow) {
- // autoBillInvoice will check for changes to ACH invoices, so we're not checking here
+ if ($invoice->getAutoBillEnabled() && $invoice->client->autoBillLater()) {
+ $this->info('Processing Invoice '.$invoice->id.' - Should bill '.($billNow ? 'YES' : 'NO'));
$this->paymentService->autoBillInvoice($invoice);
}
}
diff --git a/app/Http/Controllers/AccountGatewayController.php b/app/Http/Controllers/AccountGatewayController.php
index 1d9a05e761a2..e0f6ba9b5754 100644
--- a/app/Http/Controllers/AccountGatewayController.php
+++ b/app/Http/Controllers/AccountGatewayController.php
@@ -62,7 +62,6 @@ class AccountGatewayController extends BaseController
$data['title'] = trans('texts.edit_gateway') . ' - ' . $accountGateway->gateway->name;
$data['config'] = $config;
$data['hiddenFields'] = Gateway::$hiddenFields;
- $data['paymentTypeId'] = $accountGateway->getPaymentType();
$data['selectGateways'] = Gateway::where('id', '=', $accountGateway->gateway_id)->get();
return View::make('accounts.account_gateway', $data);
@@ -82,7 +81,7 @@ class AccountGatewayController extends BaseController
* Displays the form for account creation
*
*/
- public function create($showWepay = true)
+ public function create()
{
if ( ! \Request::secure() && ! Utils::isNinjaDev()) {
Session::flash('warning', trans('texts.enable_https'));
@@ -90,10 +89,10 @@ class AccountGatewayController extends BaseController
$account = Auth::user()->account;
$accountGatewaysIds = $account->gatewayIds();
- $showWepay = filter_var($showWepay, FILTER_VALIDATE_BOOLEAN);
+ $otherProviders = Input::get('other_providers');
if ( ! Utils::isNinja() || Gateway::hasStandardGateway($accountGatewaysIds)) {
- $showWepay = false;
+ $otherProviders = true;
}
$data = self::getViewModel();
@@ -101,14 +100,14 @@ class AccountGatewayController extends BaseController
$data['method'] = 'POST';
$data['title'] = trans('texts.add_gateway');
- if ($showWepay) {
- return View::make('accounts.account_gateway_wepay', $data);
- } else {
+ if ($otherProviders) {
$data['primaryGateways'] = Gateway::primary($accountGatewaysIds)->orderBy('name', 'desc')->get();
$data['secondaryGateways'] = Gateway::secondary($accountGatewaysIds)->orderBy('name')->get();
$data['hiddenFields'] = Gateway::$hiddenFields;
return View::make('accounts.account_gateway', $data);
+ } else {
+ return View::make('accounts.account_gateway_wepay', $data);
}
}
diff --git a/app/Http/Controllers/ClientController.php b/app/Http/Controllers/ClientController.php
index 8a451113115e..66a5e1e25e0d 100644
--- a/app/Http/Controllers/ClientController.php
+++ b/app/Http/Controllers/ClientController.php
@@ -129,6 +129,8 @@ class ClientController extends BaseController
$actionLinks[] = ['label' => trans('texts.enter_expense'), 'url' => URL::to('/expenses/create/0/'.$client->public_id)];
}
+ $token = $client->getGatewayToken();
+
$data = array(
'actionLinks' => $actionLinks,
'showBreadcrumbs' => false,
@@ -138,8 +140,8 @@ class ClientController extends BaseController
'hasRecurringInvoices' => Invoice::scope()->where('is_recurring', '=', true)->whereClientId($client->id)->count() > 0,
'hasQuotes' => Invoice::scope()->invoiceType(INVOICE_TYPE_QUOTE)->whereClientId($client->id)->count() > 0,
'hasTasks' => Task::scope()->whereClientId($client->id)->count() > 0,
- 'gatewayLink' => $client->getGatewayLink($accountGateway),
- 'gateway' => $accountGateway
+ 'gatewayLink' => $token ? $token->gatewayLink() : false,
+ 'gatewayName' => $token ? $token->gatewayName() : false,
);
return View::make('clients.show', $data);
diff --git a/app/Http/Controllers/ClientPortalController.php b/app/Http/Controllers/ClientPortalController.php
index ba088df49e91..493ae23c5f0c 100644
--- a/app/Http/Controllers/ClientPortalController.php
+++ b/app/Http/Controllers/ClientPortalController.php
@@ -98,7 +98,7 @@ class ClientPortalController extends BaseController
]);
$data = array();
- $paymentTypes = $this->getPaymentTypes($client, $invitation);
+ $paymentTypes = $this->getPaymentTypes($account, $client, $invitation);
$paymentURL = '';
if (count($paymentTypes) == 1) {
$paymentURL = $paymentTypes[0]['url'];
@@ -107,11 +107,9 @@ class ClientPortalController extends BaseController
}
}
- if ($braintreeGateway = $account->getGatewayConfig(GATEWAY_BRAINTREE)){
- if($braintreeGateway->getPayPalEnabled()) {
- $data['braintreeClientToken'] = $this->paymentService->getBraintreeClientToken($account);
- }
- } elseif ($wepayGateway = $account->getGatewayConfig(GATEWAY_WEPAY)){
+ $paymentDriver = $account->paymentDriver($invitation, GATEWAY_TYPE_CREDIT_CARD);
+
+ if ($wepayGateway = $account->getGatewayConfig(GATEWAY_WEPAY)){
$data['enableWePayACH'] = $wepayGateway->getAchEnabled();
}
@@ -123,19 +121,6 @@ class ClientPortalController extends BaseController
$showApprove = false;
}
- // Checkout.com requires first getting a payment token
- $checkoutComToken = false;
- $checkoutComKey = false;
- $checkoutComDebug = false;
- if ($accountGateway = $account->getGatewayConfig(GATEWAY_CHECKOUT_COM)) {
- $checkoutComDebug = $accountGateway->getConfigField('testMode');
- if ($checkoutComToken = $this->paymentService->getCheckoutComToken($invitation)) {
- $checkoutComKey = $accountGateway->getConfigField('publicApiKey');
- $invitation->transaction_reference = $checkoutComToken;
- $invitation->save();
- }
- }
-
$data += array(
'account' => $account,
'showApprove' => $showApprove,
@@ -147,9 +132,9 @@ class ClientPortalController extends BaseController
'contact' => $contact,
'paymentTypes' => $paymentTypes,
'paymentURL' => $paymentURL,
- 'checkoutComToken' => $checkoutComToken,
- 'checkoutComKey' => $checkoutComKey,
- 'checkoutComDebug' => $checkoutComDebug,
+ 'transactionToken' => $paymentDriver->createTransactionToken(),
+ 'partialView' => $paymentDriver->partialView(),
+ 'accountGateway' => $paymentDriver->accountGateway,
'phantomjs' => Input::has('phantomjs'),
);
@@ -164,7 +149,7 @@ class ClientPortalController extends BaseController
return View::make('invoices.view', $data);
}
-
+
public function contactIndex($contactKey) {
if (!$contact = Contact::where('contact_key', '=', $contactKey)->first()) {
return $this->returnError();
@@ -177,95 +162,17 @@ class ClientPortalController extends BaseController
return redirect()->to($client->account->enable_client_portal?'/client/dashboard':'/client/invoices/');
}
- private function getPaymentTypes($client, $invitation)
+ private function getPaymentTypes($account, $client, $invitation)
{
- $paymentTypes = [];
- $account = $client->account;
+ $links = [];
- $paymentMethods = $this->paymentService->getClientPaymentMethods($client);
-
- if ($paymentMethods) {
- foreach ($paymentMethods as $paymentMethod) {
- if ($paymentMethod->payment_type_id != PAYMENT_TYPE_ACH || $paymentMethod->status == PAYMENT_METHOD_STATUS_VERIFIED) {
- $code = htmlentities(str_replace(' ', '', strtolower($paymentMethod->payment_type->name)));
- $html = '';
-
- if ($paymentMethod->payment_type_id == PAYMENT_TYPE_ACH) {
- if ($paymentMethod->bank_name) {
- $html = '
' . htmlentities($paymentMethod->bank_name) . '
';
- } else {
- $html = '
';
- }
- } elseif ($paymentMethod->payment_type_id == PAYMENT_TYPE_ID_PAYPAL) {
- $html = '
';
- } else {
- $html = '
';
- }
-
- $url = URL::to("/payment/{$invitation->invitation_key}/token/".$paymentMethod->public_id);
-
- if ($paymentMethod->payment_type_id == PAYMENT_TYPE_ID_PAYPAL) {
- $html .= ' '.$paymentMethod->email.'';
- $url .= '#braintree_paypal';
- } elseif ($paymentMethod->payment_type_id != PAYMENT_TYPE_ACH) {
- $html .= ''.trans('texts.card_expiration', array('expires' => Utils::fromSqlDate($paymentMethod->expiration, false)->format('m/y'))).'
';
- $html .= '•••'.$paymentMethod->last4.'
';
- } else {
- $html .= '';
- $html .= '•••'.$paymentMethod->last4.'
';
- }
-
- $paymentTypes[] = [
- 'url' => $url,
- 'label' => $html,
- ];
- }
- }
+ foreach ($account->account_gateways as $accountGateway) {
+ $paymentDriver = $accountGateway->paymentDriver($invitation);
+ $links = array_merge($links, $paymentDriver->tokenLinks());
+ $links = array_merge($links, $paymentDriver->paymentLinks());
}
-
- foreach(Gateway::$paymentTypes as $type) {
- if ($type == PAYMENT_TYPE_STRIPE) {
- continue;
- }
- if ($gateway = $account->getGatewayByType($type)) {
- if ($type == PAYMENT_TYPE_DIRECT_DEBIT) {
- if ($gateway->gateway_id == GATEWAY_STRIPE) {
- $type = PAYMENT_TYPE_STRIPE_ACH;
- } elseif ($gateway->gateway_id == GATEWAY_WEPAY) {
- $type = PAYMENT_TYPE_WEPAY_ACH;
- }
- } elseif ($type == PAYMENT_TYPE_PAYPAL && $gateway->gateway_id == GATEWAY_BRAINTREE) {
- $type = PAYMENT_TYPE_BRAINTREE_PAYPAL;
- } elseif ($type == PAYMENT_TYPE_CREDIT_CARD && $gateway->gateway_id == GATEWAY_STRIPE) {
- $type = PAYMENT_TYPE_STRIPE_CREDIT_CARD;
- }
-
- $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")';
- }
-
- if ($type == PAYMENT_TYPE_STRIPE_CREDIT_CARD) {
- $label = trans('texts.' . strtolower(PAYMENT_TYPE_CREDIT_CARD));
- } elseif ($type == PAYMENT_TYPE_STRIPE_ACH || $type == PAYMENT_TYPE_WEPAY_ACH) {
- $label = trans('texts.' . strtolower(PAYMENT_TYPE_DIRECT_DEBIT));
- } elseif ($type == PAYMENT_TYPE_BRAINTREE_PAYPAL) {
- $label = trans('texts.' . strtolower(PAYMENT_TYPE_PAYPAL));
- } else {
- $label = trans('texts.' . strtolower($type));
- }
-
- $paymentTypes[] = [
- 'url' => $url, 'label' => $label
- ];
- }
- }
-
- return $paymentTypes;
+ return $links;
}
public function download($invitationKey)
@@ -303,6 +210,9 @@ class ClientPortalController extends BaseController
return $this->returnError();
}
+ $paymentDriver = $account->paymentDriver(false, GATEWAY_TYPE_TOKEN);
+ $customer = $paymentDriver->customer($client->id);
+
$data = [
'color' => $color,
'contact' => $contact,
@@ -310,15 +220,10 @@ class ClientPortalController extends BaseController
'client' => $client,
'clientFontUrl' => $account->getFontsUrl(),
'gateway' => $account->getTokenGateway(),
- 'paymentMethods' => $this->paymentService->getClientPaymentMethods($client),
+ 'paymentMethods' => $customer ? $customer->payment_methods : false,
+ 'transactionToken' => $paymentDriver->createTransactionToken(),
];
- if ($braintreeGateway = $account->getGatewayConfig(GATEWAY_BRAINTREE)){
- if($braintreeGateway->getPayPalEnabled()) {
- $data['braintreeClientToken'] = $this->paymentService->getBraintreeClientToken($account);
- }
- }
-
return response()->view('invited.dashboard', $data);
}
@@ -767,7 +672,9 @@ class ClientPortalController extends BaseController
$client = $contact->client;
$account = $client->account;
- $paymentMethods = $this->paymentService->getClientPaymentMethods($client);
+
+ $paymentDriver = $account->paymentDriver(false, GATEWAY_TYPE_TOKEN);
+ $customer = $paymentDriver->customer($client->id);
$data = array(
'account' => $account,
@@ -776,17 +683,12 @@ class ClientPortalController extends BaseController
'client' => $client,
'clientViewCSS' => $account->clientViewCSS(),
'clientFontUrl' => $account->getFontsUrl(),
- 'paymentMethods' => $paymentMethods,
+ 'paymentMethods' => $customer ? $customer->payment_methods : false,
'gateway' => $account->getTokenGateway(),
- 'title' => trans('texts.payment_methods')
+ 'title' => trans('texts.payment_methods'),
+ 'transactionToken' => $paymentDriver->createTransactionToken(),
);
- if ($braintreeGateway = $account->getGatewayConfig(GATEWAY_BRAINTREE)){
- if($braintreeGateway->getPayPalEnabled()) {
- $data['braintreeClientToken'] = $this->paymentService->getBraintreeClientToken($account);
- }
- }
-
return response()->view('payments.paymentmethods', $data);
}
@@ -801,7 +703,10 @@ class ClientPortalController extends BaseController
}
$client = $contact->client;
- $result = $this->paymentService->verifyClientPaymentMethod($client, $publicId, $amount1, $amount2);
+ $account = $client->account;
+
+ $paymentDriver = $account->paymentDriver(null, GATEWAY_TYPE_BANK_TRANSFER);
+ $result = $paymentDriver->verifyBankAccount($client, $publicId, $amount1, $amount2);
if (is_string($result)) {
Session::flash('error', $result);
@@ -809,7 +714,7 @@ class ClientPortalController extends BaseController
Session::flash('message', trans('texts.payment_method_verified'));
}
- return redirect()->to($client->account->enable_client_portal?'/client/dashboard':'/client/paymentmethods/');
+ return redirect()->to($account->enable_client_portal?'/client/dashboard':'/client/payment_methods/');
}
public function removePaymentMethod($publicId)
@@ -819,161 +724,48 @@ class ClientPortalController extends BaseController
}
$client = $contact->client;
- $result = $this->paymentService->removeClientPaymentMethod($client, $publicId);
+ $account = $contact->account;
- if (is_string($result)) {
- Session::flash('error', $result);
- } else {
+ $paymentDriver = $account->paymentDriver(false, GATEWAY_TYPE_TOKEN);
+ $paymentMethod = PaymentMethod::clientId($client->id)
+ ->wherePublicId($publicId)
+ ->firstOrFail();
+
+ try {
+ $paymentDriver->removePaymentMethod($paymentMethod);
Session::flash('message', trans('texts.payment_method_removed'));
+ } catch (Exception $exception) {
+ Session::flash('error', $exception->getMessage());
}
- return redirect()->to($client->account->enable_client_portal?'/client/dashboard':'/client/paymentmethods/');
+ return redirect()->to($client->account->enable_client_portal?'/client/dashboard':'/client/payment_methods/');
}
-
- public function addPaymentMethod($paymentType, $token=false)
- {
- if (!$contact = $this->getContact()) {
- return $this->returnError();
- }
-
- $client = $contact->client;
- $account = $client->account;
-
- $typeLink = $paymentType;
- $paymentType = 'PAYMENT_TYPE_' . strtoupper($paymentType);
- $accountGateway = $client->account->getTokenGateway();
- $gateway = $accountGateway->gateway;
-
- if ($token && $paymentType == PAYMENT_TYPE_BRAINTREE_PAYPAL) {
- $sourceReference = $this->paymentService->createToken($paymentType, $this->paymentService->createGateway($accountGateway), array('token'=>$token), $accountGateway, $client, $contact->id);
-
- if(empty($sourceReference)) {
- $this->paymentMethodError('Token-No-Ref', $this->paymentService->lastError, $accountGateway);
- } else {
- Session::flash('message', trans('texts.payment_method_added'));
- }
- return redirect()->to($account->enable_client_portal?'/client/dashboard':'/client/paymentmethods/');
- }
-
- $acceptedCreditCardTypes = $accountGateway->getCreditcardTypes();
-
-
- $data = [
- 'showBreadcrumbs' => false,
- 'client' => $client,
- 'contact' => $contact,
- 'gateway' => $gateway,
- 'accountGateway' => $accountGateway,
- 'acceptedCreditCardTypes' => $acceptedCreditCardTypes,
- 'paymentType' => $paymentType,
- 'countries' => Cache::get('countries'),
- 'currencyId' => $client->getCurrencyId(),
- 'currencyCode' => $client->currency ? $client->currency->code : ($account->currency ? $account->currency->code : 'USD'),
- 'account' => $account,
- 'url' => URL::to('client/paymentmethods/add/'.$typeLink),
- 'clientFontUrl' => $account->getFontsUrl(),
- 'showAddress' => $accountGateway->show_address,
- 'paymentTitle' => trans('texts.add_payment_method'),
- 'sourceId' => $token,
- ];
-
- $details = json_decode(Input::get('details'));
- $data['details'] = $details;
-
- if ($paymentType == PAYMENT_TYPE_STRIPE_ACH) {
- $data['currencies'] = Cache::get('currencies');
- }
-
- if ($gateway->id == GATEWAY_BRAINTREE) {
- $data['braintreeClientToken'] = $this->paymentService->getBraintreeClientToken($account);
- }
-
- if(!empty($data['braintreeClientToken']) || $accountGateway->getPublishableStripeKey()|| ($accountGateway->gateway_id == GATEWAY_WEPAY && $paymentType != PAYMENT_TYPE_WEPAY_ACH)) {
- $data['tokenize'] = true;
- }
-
- return View::make('payments.add_paymentmethod', $data);
- }
-
- public function postAddPaymentMethod($paymentType)
- {
- if (!$contact = $this->getContact()) {
- return $this->returnError();
- }
-
- $client = $contact->client;
-
- $typeLink = $paymentType;
- $paymentType = 'PAYMENT_TYPE_' . strtoupper($paymentType);
- $account = $client->account;
-
- $accountGateway = $account->getGatewayByType($paymentType);
- $sourceToken = Input::get('sourceToken');
-
- if (($validator = PaymentController::processPaymentClientDetails($client, $accountGateway, $paymentType)) !== true) {
- return Redirect::to('client/paymentmethods/add/' . $typeLink.'/'.$sourceToken)
- ->withErrors($validator)
- ->withInput(Request::except('cvv'));
- }
-
- if ($sourceToken) {
- $details = array('token' => $sourceToken);
- } elseif (Input::get('plaidPublicToken')) {
- $usingPlaid = true;
- $details = array('plaidPublicToken' => Input::get('plaidPublicToken'), 'plaidAccountId' => Input::get('plaidAccountId'));
- }
-
- if (($paymentType == PAYMENT_TYPE_STRIPE_ACH || $paymentType == PAYMENT_TYPE_WEPAY_ACH) && !Input::get('authorize_ach')) {
- Session::flash('error', trans('texts.ach_authorization_required'));
- return Redirect::to('client/paymentmethods/add/' . $typeLink.'/'.$sourceToken)->withInput(Request::except('cvv'));
- }
-
- if ($paymentType == PAYMENT_TYPE_WEPAY_ACH && !Input::get('tos_agree')) {
- Session::flash('error', trans('texts.wepay_payment_tos_agree_required'));
- return Redirect::to('client/paymentmethods/add/' . $typeLink.'/'.$sourceToken)->withInput(Request::except('cvv'));
- }
-
- if (!empty($details)) {
- $gateway = $this->paymentService->createGateway($accountGateway);
- $sourceReference = $this->paymentService->createToken($paymentType, $gateway, $details, $accountGateway, $client, $contact->id);
- } else {
- return Redirect::to('client/paymentmethods/add/' . $typeLink.'/'.$sourceToken)->withInput(Request::except('cvv'));
- }
-
- if(empty($sourceReference)) {
- $this->paymentMethodError('Token-No-Ref', $this->paymentService->lastError, $accountGateway);
- return Redirect::to('client/paymentmethods/add/' . $typeLink.'/'.$sourceToken)->withInput(Request::except('cvv'));
- } else if ($paymentType == PAYMENT_TYPE_STRIPE_ACH && empty($usingPlaid) ) {
- // The user needs to complete verification
- Session::flash('message', trans('texts.bank_account_verification_next_steps'));
- return Redirect::to($account->enable_client_portal?'/client/dashboard':'/client/paymentmethods/');
- } else {
- Session::flash('message', trans('texts.payment_method_added'));
- return redirect()->to($account->enable_client_portal?'/client/dashboard':'/client/paymentmethods/');
- }
- }
-
+
public function setDefaultPaymentMethod(){
if (!$contact = $this->getContact()) {
return $this->returnError();
}
$client = $contact->client;
+ $account = $client->account;
$validator = Validator::make(Input::all(), array('source' => 'required'));
if ($validator->fails()) {
- return Redirect::to($client->account->enable_client_portal?'/client/dashboard':'/client/paymentmethods/');
+ return Redirect::to($client->account->enable_client_portal?'/client/dashboard':'/client/payment_methods/');
}
- $result = $this->paymentService->setClientDefaultPaymentMethod($client, Input::get('source'));
+ $paymentDriver = $account->paymentDriver(false, GATEWAY_TYPE_TOKEN);
+ $paymentMethod = PaymentMethod::clientId($client->id)
+ ->wherePublicId(Input::get('source'))
+ ->firstOrFail();
- if (is_string($result)) {
- Session::flash('error', $result);
- } else {
- Session::flash('message', trans('texts.payment_method_set_as_default'));
- }
+ $customer = $paymentDriver->customer($client->id);
+ $customer->default_payment_method_id = $paymentMethod->id;
+ $customer->save();
- return redirect()->to($client->account->enable_client_portal?'/client/dashboard':'/client/paymentmethods/');
+ Session::flash('message', trans('texts.payment_method_set_as_default'));
+
+ return redirect()->to($client->account->enable_client_portal?'/client/dashboard':'/client/payment_methods/');
}
private function paymentMethodError($type, $error, $accountGateway = false, $exception = false)
diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php
index dcef1b61464e..28c73ba65e3c 100644
--- a/app/Http/Controllers/InvoiceController.php
+++ b/app/Http/Controllers/InvoiceController.php
@@ -200,7 +200,7 @@ class InvoiceController extends BaseController
$data = array_merge($data, self::getViewModel($invoice));
if ($invoice->isSent() && $invoice->getAutoBillEnabled() && !$invoice->isPaid()) {
- $data['autoBillChangeWarning'] = $this->paymentService->getClientRequiresDelayedAutoBill($invoice->client);
+ $data['autoBillChangeWarning'] = $invoice->client->autoBillLater();
}
if ($clone) {
diff --git a/app/Http/Controllers/NinjaController.php b/app/Http/Controllers/NinjaController.php
new file mode 100644
index 000000000000..11f9238ae6b4
--- /dev/null
+++ b/app/Http/Controllers/NinjaController.php
@@ -0,0 +1,236 @@
+accountRepo = $accountRepo;
+ $this->contactMailer = $contactMailer;
+ }
+
+ private function getLicensePaymentDetails($input, $affiliate)
+ {
+ $country = Country::find($input['country_id']);
+
+ $data = [
+ 'firstName' => $input['first_name'],
+ 'lastName' => $input['last_name'],
+ 'email' => $input['email'],
+ 'number' => $input['card_number'],
+ 'expiryMonth' => $input['expiration_month'],
+ 'expiryYear' => $input['expiration_year'],
+ 'cvv' => $input['cvv'],
+ 'billingAddress1' => $input['address1'],
+ 'billingAddress2' => $input['address2'],
+ 'billingCity' => $input['city'],
+ 'billingState' => $input['state'],
+ 'billingPostcode' => $input['postal_code'],
+ 'billingCountry' => $country->iso_3166_2,
+ 'shippingAddress1' => $input['address1'],
+ 'shippingAddress2' => $input['address2'],
+ 'shippingCity' => $input['city'],
+ 'shippingState' => $input['state'],
+ 'shippingPostcode' => $input['postal_code'],
+ 'shippingCountry' => $country->iso_3166_2
+ ];
+
+ $card = new CreditCard($data);
+
+ return [
+ 'amount' => $affiliate->price,
+ 'card' => $card,
+ 'currency' => 'USD',
+ 'returnUrl' => URL::to('license_complete'),
+ 'cancelUrl' => URL::to('/')
+ ];
+ }
+
+ public function show_license_payment()
+ {
+ if (Input::has('return_url')) {
+ Session::set('return_url', Input::get('return_url'));
+ }
+
+ if (Input::has('affiliate_key')) {
+ if ($affiliate = Affiliate::where('affiliate_key', '=', Input::get('affiliate_key'))->first()) {
+ Session::set('affiliate_id', $affiliate->id);
+ }
+ }
+
+ if (Input::has('product_id')) {
+ Session::set('product_id', Input::get('product_id'));
+ } else if (!Session::has('product_id')) {
+ Session::set('product_id', PRODUCT_ONE_CLICK_INSTALL);
+ }
+
+ if (!Session::get('affiliate_id')) {
+ return Utils::fatalError();
+ }
+
+ if (Utils::isNinjaDev() && Input::has('test_mode')) {
+ Session::set('test_mode', Input::get('test_mode'));
+ }
+
+ $account = $this->accountRepo->getNinjaAccount();
+ $account->load('account_gateways.gateway');
+ $accountGateway = $account->getGatewayByType(GATEWAY_TYPE_CREDIT_CARD);
+ $gateway = $accountGateway->gateway;
+ $acceptedCreditCardTypes = $accountGateway->getCreditcardTypes();
+
+ $affiliate = Affiliate::find(Session::get('affiliate_id'));
+
+ $data = [
+ 'showBreadcrumbs' => false,
+ 'hideHeader' => true,
+ 'url' => 'license',
+ 'amount' => $affiliate->price,
+ 'client' => false,
+ 'contact' => false,
+ 'gateway' => $gateway,
+ 'account' => $account,
+ 'accountGateway' => $accountGateway,
+ 'acceptedCreditCardTypes' => $acceptedCreditCardTypes,
+ 'countries' => Cache::get('countries'),
+ 'currencyId' => 1,
+ 'currencyCode' => 'USD',
+ 'paymentTitle' => $affiliate->payment_title,
+ 'paymentSubtitle' => $affiliate->payment_subtitle,
+ 'showAddress' => true,
+ ];
+
+ return View::make('payments.stripe.credit_card', $data);
+ }
+
+ public function do_license_payment()
+ {
+ $testMode = Session::get('test_mode') === 'true';
+
+ $rules = array(
+ 'first_name' => 'required',
+ 'last_name' => 'required',
+ 'email' => 'required',
+ 'card_number' => 'required',
+ 'expiration_month' => 'required',
+ 'expiration_year' => 'required',
+ 'cvv' => 'required',
+ 'address1' => 'required',
+ 'city' => 'required',
+ 'state' => 'required',
+ 'postal_code' => 'required',
+ 'country_id' => 'required',
+ );
+
+ $validator = Validator::make(Input::all(), $rules);
+
+ if ($validator->fails()) {
+ return redirect()->to('license')
+ ->withErrors($validator)
+ ->withInput();
+ }
+
+ $account = $this->accountRepo->getNinjaAccount();
+ $account->load('account_gateways.gateway');
+ $accountGateway = $account->getGatewayByType(GATEWAY_TYPE_CREDIT_CARD);
+
+ try {
+ $affiliate = Affiliate::find(Session::get('affiliate_id'));
+
+ if ($testMode) {
+ $ref = 'TEST_MODE';
+ } else {
+ $details = self::getLicensePaymentDetails(Input::all(), $affiliate);
+
+ $gateway = Omnipay::create($accountGateway->gateway->provider);
+ $gateway->initialize((array) $accountGateway->getConfig());
+ $response = $gateway->purchase($details)->send();
+
+ $ref = $response->getTransactionReference();
+
+ if (!$response->isSuccessful() || !$ref) {
+ $this->error('License', $response->getMessage(), $accountGateway);
+ return redirect()->to('license')->withInput();
+ }
+ }
+
+ $licenseKey = Utils::generateLicense();
+
+ $license = new License();
+ $license->first_name = Input::get('first_name');
+ $license->last_name = Input::get('last_name');
+ $license->email = Input::get('email');
+ $license->transaction_reference = $ref;
+ $license->license_key = $licenseKey;
+ $license->affiliate_id = Session::get('affiliate_id');
+ $license->product_id = Session::get('product_id');
+ $license->save();
+
+ $data = [
+ 'message' => $affiliate->payment_subtitle,
+ 'license' => $licenseKey,
+ 'hideHeader' => true,
+ 'productId' => $license->product_id,
+ 'price' => $affiliate->price,
+ ];
+
+ $name = "{$license->first_name} {$license->last_name}";
+ $this->contactMailer->sendLicensePaymentConfirmation($name, $license->email, $affiliate->price, $license->license_key, $license->product_id);
+
+ if (Session::has('return_url')) {
+ $data['redirectTo'] = Session::get('return_url')."?license_key={$license->license_key}&product_id=".Session::get('product_id');
+ $data['message'] = "Redirecting to " . Session::get('return_url');
+ }
+
+ return View::make('public.license', $data);
+ } catch (\Exception $e) {
+ $this->error('License-Uncaught', false, $accountGateway, $e);
+ return redirect()->to('license')->withInput();
+ }
+ }
+
+ public function claim_license()
+ {
+ $licenseKey = Input::get('license_key');
+ $productId = Input::get('product_id', PRODUCT_ONE_CLICK_INSTALL);
+
+ $license = License::where('license_key', '=', $licenseKey)
+ ->where('is_claimed', '<', 10)
+ ->where('product_id', '=', $productId)
+ ->first();
+
+ if ($license) {
+ if ($license->transaction_reference != 'TEST_MODE') {
+ $license->is_claimed = $license->is_claimed + 1;
+ $license->save();
+ }
+
+ if ($productId == PRODUCT_INVOICE_DESIGNS) {
+ return file_get_contents(storage_path() . '/invoice_designs.txt');
+ } else {
+ // temporary fix to enable previous version to work
+ if (Input::get('get_date')) {
+ return $license->created_at->format('Y-m-d');
+ } else {
+ return 'valid';
+ }
+ }
+ } else {
+ return RESULT_FAILURE;
+ }
+ }
+
+}
diff --git a/app/Http/Controllers/OnlinePaymentController.php b/app/Http/Controllers/OnlinePaymentController.php
new file mode 100644
index 000000000000..5b1229484dd8
--- /dev/null
+++ b/app/Http/Controllers/OnlinePaymentController.php
@@ -0,0 +1,333 @@
+paymentService = $paymentService;
+ $this->userMailer = $userMailer;
+ }
+
+ public function showPayment($invitationKey, $gatewayType = false, $sourceId = false)
+ {
+ $invitation = Invitation::with('invoice.invoice_items', 'invoice.client.currency', 'invoice.client.account.account_gateways.gateway')
+ ->where('invitation_key', '=', $invitationKey)->firstOrFail();
+
+ if ( ! $gatewayType) {
+ $gatewayType = Session::get($invitation->id . 'gateway_type');
+ }
+
+ $paymentDriver = $invitation->account->paymentDriver($invitation, $gatewayType);
+
+ try {
+ return $paymentDriver->startPurchase(Input::all(), $sourceId);
+ } catch (Exception $exception) {
+ return $this->error($paymentDriver, $exception);
+ }
+ }
+
+ public function doPayment(CreateOnlinePaymentRequest $request)
+ {
+ $invitation = $request->invitation;
+ $gatewayType = Session::get($invitation->id . 'gateway_type');
+ $paymentDriver = $invitation->account->paymentDriver($invitation, $gatewayType);
+
+ try {
+ $paymentDriver->completeOnsitePurchase($request->all());
+
+ if ($paymentDriver->isTwoStep()) {
+ Session::flash('warning', trans('texts.bank_account_verification_next_steps'));
+ } else {
+ Session::flash('message', trans('texts.applied_payment'));
+ }
+ return redirect()->to('view/' . $invitation->invitation_key);
+ } catch (Exception $exception) {
+ return $this->error($paymentDriver, $exception);
+ }
+ }
+
+ public function offsitePayment($invitationKey = false, $gatewayType = false)
+ {
+ $invitationKey = $invitationKey ?: Session::get('invitation_key');
+ $invitation = Invitation::with('invoice.invoice_items', 'invoice.client.currency', 'invoice.client.account.account_gateways.gateway')
+ ->where('invitation_key', '=', $invitationKey)->firstOrFail();
+
+ $gatewayType = $gatewayType ?: Session::get($invitation->id . 'gateway_type');
+ $paymentDriver = $invitation->account->paymentDriver($invitation, $gatewayType);
+
+ if ($error = Input::get('error_description') ?: Input::get('error')) {
+ return $this->error($paymentDriver, $error);
+ }
+
+ try {
+ $paymentDriver->completeOffsitePurchase(Input::all());
+ Session::flash('message', trans('texts.applied_payment'));
+ return redirect()->to('view/' . $invitation->invitation_key);
+ } catch (Exception $exception) {
+ return $this->error($paymentDriver, $exception);
+ }
+ }
+
+ private function error($paymentDriver, $exception)
+ {
+ if (is_string($exception)) {
+ $displayError = $exception;
+ $logError = $exception;
+ } else {
+ $displayError = $exception->getMessage();
+ $logError = Utils::getErrorString($exception);
+ }
+
+ $message = sprintf('%s: %s', ucwords($paymentDriver->providerName()), $displayError);
+ Session::flash('error', $message);
+
+ $message = sprintf('Payment Error [%s]: %s', $paymentDriver->providerName(), $logError);
+ Utils::logError($message, 'PHP', true);
+
+ return redirect()->to('view/' . $paymentDriver->invitation->invitation_key);
+ }
+
+ public function getBankInfo($routingNumber) {
+ if (strlen($routingNumber) != 9 || !preg_match('/\d{9}/', $routingNumber)) {
+ return response()->json([
+ 'message' => 'Invalid routing number',
+ ], 400);
+ }
+
+ $data = PaymentMethod::lookupBankData($routingNumber);
+
+ if (is_string($data)) {
+ return response()->json([
+ 'message' => $data,
+ ], 500);
+ } elseif (!empty($data)) {
+ return response()->json($data);
+ }
+
+ return response()->json([
+ 'message' => 'Bank not found',
+ ], 404);
+ }
+
+ public function handlePaymentWebhook($accountKey, $gatewayId)
+ {
+ $gatewayId = intval($gatewayId);
+
+ $account = Account::where('accounts.account_key', '=', $accountKey)->first();
+
+ if (!$account) {
+ return response()->json([
+ 'message' => 'Unknown account',
+ ], 404);
+ }
+
+ $accountGateway = $account->getGatewayConfig(intval($gatewayId));
+
+ if (!$accountGateway) {
+ return response()->json([
+ 'message' => 'Unknown gateway',
+ ], 404);
+ }
+
+ switch($gatewayId) {
+ case GATEWAY_STRIPE:
+ return $this->handleStripeWebhook($accountGateway);
+ case GATEWAY_WEPAY:
+ return $this->handleWePayWebhook($accountGateway);
+ default:
+ return response()->json([
+ 'message' => 'Unsupported gateway',
+ ], 404);
+ }
+ }
+
+ protected function handleWePayWebhook($accountGateway) {
+ $data = Input::all();
+ $accountId = $accountGateway->account_id;
+
+ foreach (array_keys($data) as $key) {
+ if ('_id' == substr($key, -3)) {
+ $objectType = substr($key, 0, -3);
+ $objectId = $data[$key];
+ break;
+ }
+ }
+
+ if (!isset($objectType)) {
+ return response()->json([
+ 'message' => 'Could not find object id parameter',
+ ], 400);
+ }
+
+ if ($objectType == 'credit_card') {
+ $paymentMethod = PaymentMethod::scope(false, $accountId)->where('source_reference', '=', $objectId)->first();
+
+ if (!$paymentMethod) {
+ return array('message' => 'Unknown payment method');
+ }
+
+ $wepay = \Utils::setupWePay($accountGateway);
+ $source = $wepay->request('credit_card', array(
+ 'client_id' => WEPAY_CLIENT_ID,
+ 'client_secret' => WEPAY_CLIENT_SECRET,
+ 'credit_card_id' => intval($objectId),
+ ));
+
+ if ($source->state == 'deleted') {
+ $paymentMethod->delete();
+ } else {
+ $this->paymentService->convertPaymentMethodFromWePay($source, null, $paymentMethod)->save();
+ }
+
+ return array('message' => 'Processed successfully');
+ } elseif ($objectType == 'account') {
+ $config = $accountGateway->getConfig();
+ if ($config->accountId != $objectId) {
+ return array('message' => 'Unknown account');
+ }
+
+ $wepay = \Utils::setupWePay($accountGateway);
+ $wepayAccount = $wepay->request('account', array(
+ 'account_id' => intval($objectId),
+ ));
+
+ if ($wepayAccount->state == 'deleted') {
+ $accountGateway->delete();
+ } else {
+ $config->state = $wepayAccount->state;
+ $accountGateway->setConfig($config);
+ $accountGateway->save();
+ }
+
+ return array('message' => 'Processed successfully');
+ } elseif ($objectType == 'checkout') {
+ $payment = Payment::scope(false, $accountId)->where('transaction_reference', '=', $objectId)->first();
+
+ if (!$payment) {
+ return array('message' => 'Unknown payment');
+ }
+
+ $wepay = \Utils::setupWePay($accountGateway);
+ $checkout = $wepay->request('checkout', array(
+ 'checkout_id' => intval($objectId),
+ ));
+
+ if ($checkout->state == 'refunded') {
+ $payment->recordRefund();
+ } elseif (!empty($checkout->refund) && !empty($checkout->refund->amount_refunded) && ($checkout->refund->amount_refunded - $payment->refunded) > 0) {
+ $payment->recordRefund($checkout->refund->amount_refunded - $payment->refunded);
+ }
+
+ if ($checkout->state == 'captured') {
+ $payment->markComplete();
+ } elseif ($checkout->state == 'cancelled') {
+ $payment->markCancelled();
+ } elseif ($checkout->state == 'failed') {
+ $payment->markFailed();
+ }
+
+ return array('message' => 'Processed successfully');
+ } else {
+ return array('message' => 'Ignoring event');
+ }
+ }
+
+ protected function handleStripeWebhook($accountGateway) {
+ $eventId = Input::get('id');
+ $eventType= Input::get('type');
+ $accountId = $accountGateway->account_id;
+
+ if (!$eventId) {
+ return response()->json(['message' => 'Missing event id'], 400);
+ }
+
+ if (!$eventType) {
+ return response()->json(['message' => 'Missing event type'], 400);
+ }
+
+ $supportedEvents = array(
+ 'charge.failed',
+ 'charge.succeeded',
+ 'customer.source.updated',
+ 'customer.source.deleted',
+ );
+
+ if (!in_array($eventType, $supportedEvents)) {
+ return array('message' => 'Ignoring event');
+ }
+
+ // Fetch the event directly from Stripe for security
+ $eventDetails = $this->paymentService->makeStripeCall($accountGateway, 'GET', 'events/'.$eventId);
+
+ if (is_string($eventDetails) || !$eventDetails) {
+ return response()->json([
+ 'message' => $eventDetails ? $eventDetails : 'Could not get event details.',
+ ], 500);
+ }
+
+ if ($eventType != $eventDetails['type']) {
+ return response()->json(['message' => 'Event type mismatch'], 400);
+ }
+
+ if (!$eventDetails['pending_webhooks']) {
+ return response()->json(['message' => 'This is not a pending event'], 400);
+ }
+
+
+ if ($eventType == 'charge.failed' || $eventType == 'charge.succeeded') {
+ $charge = $eventDetails['data']['object'];
+ $transactionRef = $charge['id'];
+
+ $payment = Payment::scope(false, $accountId)->where('transaction_reference', '=', $transactionRef)->first();
+
+ if (!$payment) {
+ return array('message' => 'Unknown payment');
+ }
+
+ if ($eventType == 'charge.failed') {
+ if (!$payment->isFailed()) {
+ $payment->markFailed($charge['failure_message']);
+ $this->userMailer->sendNotification($payment->user, $payment->invoice, 'payment_failed', $payment);
+ }
+ } elseif ($eventType == 'charge.succeeded') {
+ $payment->markComplete();
+ } elseif ($eventType == 'charge.refunded') {
+ $payment->recordRefund($charge['amount_refunded'] / 100 - $payment->refunded);
+ }
+ } elseif($eventType == 'customer.source.updated' || $eventType == 'customer.source.deleted') {
+ $source = $eventDetails['data']['object'];
+ $sourceRef = $source['id'];
+
+ $paymentMethod = PaymentMethod::scope(false, $accountId)->where('source_reference', '=', $sourceRef)->first();
+
+ if (!$paymentMethod) {
+ return array('message' => 'Unknown payment method');
+ }
+
+ if ($eventType == 'customer.source.deleted') {
+ $paymentMethod->delete();
+ } elseif ($eventType == 'customer.source.updated') {
+ $this->paymentService->convertPaymentMethodFromStripe($source, null, $paymentMethod)->save();
+ }
+ }
+
+ return array('message' => 'Processed successfully');
+ }
+
+}
diff --git a/app/Http/Controllers/PaymentController.php b/app/Http/Controllers/PaymentController.php
index 3dabd2a4c2aa..fe16fcbb9ce4 100644
--- a/app/Http/Controllers/PaymentController.php
+++ b/app/Http/Controllers/PaymentController.php
@@ -1,33 +1,16 @@
paymentRepo = $paymentRepo;
- $this->invoiceRepo = $invoiceRepo;
- $this->accountRepo = $accountRepo;
$this->contactMailer = $contactMailer;
$this->paymentService = $paymentService;
- $this->userMailer = $userMailer;
}
public function index()
@@ -121,606 +99,6 @@ class PaymentController extends BaseController
return View::make('payments.edit', $data);
}
- private function getLicensePaymentDetails($input, $affiliate)
- {
- $data = $this->paymentService->convertInputForOmnipay($input);
- $card = new CreditCard($data);
-
- return [
- 'amount' => $affiliate->price,
- 'card' => $card,
- 'currency' => 'USD',
- 'returnUrl' => URL::to('license_complete'),
- 'cancelUrl' => URL::to('/')
- ];
- }
-
- public function show_payment($invitationKey, $paymentType = false, $sourceId = false)
- {
- $invitation = Invitation::with('invoice.invoice_items', 'invoice.client.currency', 'invoice.client.account.account_gateways.gateway')->where('invitation_key', '=', $invitationKey)->firstOrFail();
- $invoice = $invitation->invoice;
- $client = $invoice->client;
- $account = $client->account;
- $useToken = false;
-
- if ($paymentType) {
- $paymentType = 'PAYMENT_TYPE_' . strtoupper($paymentType);
- } else {
- $paymentType = Session::get($invitation->id . 'payment_type') ?:
- $account->account_gateways[0]->getPaymentType();
- }
-
- $data = array();
-
- Session::put($invitation->id.'payment_ref', $invoice->id.'_'.uniqid());
-
- $details = json_decode(Input::get('details'));
- $data['details'] = $details;
-
-
- if ($paymentType == PAYMENT_TYPE_BRAINTREE_PAYPAL) {
- if ($deviceData = Input::get('device_data')) {
- Session::put($invitation->id . 'device_data', $deviceData);
- }
-
- Session::put($invitation->id . 'payment_type', PAYMENT_TYPE_BRAINTREE_PAYPAL);
- if (!$sourceId || !$details) {
- return Redirect::to('view/'.$invitationKey);
- }
- } elseif ($paymentType == PAYMENT_TYPE_WEPAY_ACH) {
- Session::put($invitation->id . 'payment_type', PAYMENT_TYPE_WEPAY_ACH);
-
- if (!$sourceId) {
- return Redirect::to('view/'.$invitationKey);
- }
- } else {
- if ($paymentType == PAYMENT_TYPE_TOKEN) {
- $useToken = true;
- $accountGateway = $invoice->client->account->getTokenGateway();
- $paymentType = $accountGateway->getPaymentType();
- } else {
- $accountGateway = $invoice->client->account->getGatewayByType($paymentType);
- }
-
- Session::put($invitation->id . 'payment_type', $paymentType);
-
- $gateway = $accountGateway->gateway;
-
- $acceptedCreditCardTypes = $accountGateway->getCreditcardTypes();
-
- $isOffsite = ($paymentType != PAYMENT_TYPE_CREDIT_CARD && $accountGateway->getPaymentType() != PAYMENT_TYPE_STRIPE)
- || $gateway->id == GATEWAY_EWAY
- || $gateway->id == GATEWAY_TWO_CHECKOUT
- || $gateway->id == GATEWAY_PAYFAST
- || $gateway->id == GATEWAY_MOLLIE;
-
- // Handle offsite payments
- if ($useToken || $isOffsite) {
- if (Session::has('error')) {
- Session::reflash();
- return Redirect::to('view/' . $invitationKey);
- } else {
- return self::do_payment($invitationKey, false, $useToken, $sourceId);
- }
- }
-
- $data += [
- 'accountGateway' => $accountGateway,
- 'acceptedCreditCardTypes' => $acceptedCreditCardTypes,
- 'gateway' => $gateway,
- 'showAddress' => $accountGateway->show_address,
- ];
-
- if ($paymentType == PAYMENT_TYPE_STRIPE_ACH) {
- $data['currencies'] = Cache::get('currencies');
- }
-
- if ($gateway->id == GATEWAY_BRAINTREE) {
- $data['braintreeClientToken'] = $this->paymentService->getBraintreeClientToken($account);
- }
-
- if(!empty($data['braintreeClientToken']) || $accountGateway->getPublishableStripeKey()|| $accountGateway->gateway_id == GATEWAY_WEPAY) {
- $data['tokenize'] = true;
- }
-
- }
-
- $data += [
- 'showBreadcrumbs' => false,
- 'url' => 'payment/'.$invitationKey,
- 'amount' => $invoice->getRequestedAmount(),
- 'invoiceNumber' => $invoice->invoice_number,
- 'client' => $client,
- 'contact' => $invitation->contact,
- 'paymentType' => $paymentType,
- 'countries' => Cache::get('countries'),
- 'currencyId' => $client->getCurrencyId(),
- 'currencyCode' => $client->currency ? $client->currency->code : ($account->currency ? $account->currency->code : 'USD'),
- 'account' => $client->account,
- 'sourceId' => $sourceId,
- 'clientFontUrl' => $client->account->getFontsUrl(),
- ];
-
- return View::make('payments.add_paymentmethod', $data);
- }
-
- public function show_license_payment()
- {
- if (Input::has('return_url')) {
- Session::set('return_url', Input::get('return_url'));
- }
-
- if (Input::has('affiliate_key')) {
- if ($affiliate = Affiliate::where('affiliate_key', '=', Input::get('affiliate_key'))->first()) {
- Session::set('affiliate_id', $affiliate->id);
- }
- }
-
- if (Input::has('product_id')) {
- Session::set('product_id', Input::get('product_id'));
- } else if (!Session::has('product_id')) {
- Session::set('product_id', PRODUCT_ONE_CLICK_INSTALL);
- }
-
- if (!Session::get('affiliate_id')) {
- return Utils::fatalError();
- }
-
- if (Utils::isNinjaDev() && Input::has('test_mode')) {
- Session::set('test_mode', Input::get('test_mode'));
- }
-
- $account = $this->accountRepo->getNinjaAccount();
- $account->load('account_gateways.gateway');
- $accountGateway = $account->getGatewayByType(PAYMENT_TYPE_STRIPE_CREDIT_CARD);
- $gateway = $accountGateway->gateway;
- $acceptedCreditCardTypes = $accountGateway->getCreditcardTypes();
-
- $affiliate = Affiliate::find(Session::get('affiliate_id'));
-
- $data = [
- 'showBreadcrumbs' => false,
- 'hideHeader' => true,
- 'url' => 'license',
- 'amount' => $affiliate->price,
- 'client' => false,
- 'contact' => false,
- 'gateway' => $gateway,
- 'account' => $account,
- 'accountGateway' => $accountGateway,
- 'acceptedCreditCardTypes' => $acceptedCreditCardTypes,
- 'countries' => Cache::get('countries'),
- 'currencyId' => 1,
- 'currencyCode' => 'USD',
- 'paymentTitle' => $affiliate->payment_title,
- 'paymentSubtitle' => $affiliate->payment_subtitle,
- 'showAddress' => true,
- ];
-
- return View::make('payments.add_paymentmethod', $data);
- }
-
- public function do_license_payment()
- {
- $testMode = Session::get('test_mode') === 'true';
-
- $rules = array(
- 'first_name' => 'required',
- 'last_name' => 'required',
- 'card_number' => 'required',
- 'expiration_month' => 'required',
- 'expiration_year' => 'required',
- 'cvv' => 'required',
- 'address1' => 'required',
- 'city' => 'required',
- 'state' => 'required',
- 'postal_code' => 'required',
- 'country_id' => 'required',
- );
-
- $validator = Validator::make(Input::all(), $rules);
-
- if ($validator->fails()) {
- return Redirect::to('license')
- ->withErrors($validator)
- ->withInput();
- }
-
- $account = $this->accountRepo->getNinjaAccount();
- $account->load('account_gateways.gateway');
- $accountGateway = $account->getGatewayByType(PAYMENT_TYPE_STRIPE_CREDIT_CARD);
-
- try {
- $affiliate = Affiliate::find(Session::get('affiliate_id'));
-
- if ($testMode) {
- $ref = 'TEST_MODE';
- } else {
- $details = self::getLicensePaymentDetails(Input::all(), $affiliate);
- $response = $this->paymentService->purchase($accountGateway, $details);
- $ref = $response->getTransactionReference();
-
- if (!$response->isSuccessful() || !$ref) {
- $this->error('License', $response->getMessage(), $accountGateway);
- return Redirect::to('license')->withInput();
- }
- }
-
- $licenseKey = Utils::generateLicense();
-
- $license = new License();
- $license->first_name = Input::get('first_name');
- $license->last_name = Input::get('last_name');
- $license->email = Input::get('email');
- $license->transaction_reference = $ref;
- $license->license_key = $licenseKey;
- $license->affiliate_id = Session::get('affiliate_id');
- $license->product_id = Session::get('product_id');
- $license->save();
-
- $data = [
- 'message' => $affiliate->payment_subtitle,
- 'license' => $licenseKey,
- 'hideHeader' => true,
- 'productId' => $license->product_id,
- 'price' => $affiliate->price,
- ];
-
- $name = "{$license->first_name} {$license->last_name}";
- $this->contactMailer->sendLicensePaymentConfirmation($name, $license->email, $affiliate->price, $license->license_key, $license->product_id);
-
- if (Session::has('return_url')) {
- $data['redirectTo'] = Session::get('return_url')."?license_key={$license->license_key}&product_id=".Session::get('product_id');
- $data['message'] = "Redirecting to " . Session::get('return_url');
- }
-
- return View::make('public.license', $data);
- } catch (\Exception $e) {
- $this->error('License-Uncaught', false, $accountGateway, $e);
- return Redirect::to('license')->withInput();
- }
- }
-
- public function claim_license()
- {
- $licenseKey = Input::get('license_key');
- $productId = Input::get('product_id', PRODUCT_ONE_CLICK_INSTALL);
-
- $license = License::where('license_key', '=', $licenseKey)
- ->where('is_claimed', '<', 5)
- ->where('product_id', '=', $productId)
- ->first();
-
- if ($license) {
- if ($license->transaction_reference != 'TEST_MODE') {
- $license->is_claimed = $license->is_claimed + 1;
- $license->save();
- }
-
- if ($productId == PRODUCT_INVOICE_DESIGNS) {
- return file_get_contents(storage_path() . '/invoice_designs.txt');
- } else {
- // temporary fix to enable previous version to work
- if (Input::get('get_date')) {
- return $license->created_at->format('Y-m-d');
- } else {
- return 'valid';
- }
- }
- } else {
- return RESULT_FAILURE;
- }
- }
-
- public static function processPaymentClientDetails($client, $accountGateway, $paymentType, $onSite = true){
- $rules = ($paymentType == PAYMENT_TYPE_STRIPE_ACH || $paymentType == PAYMENT_TYPE_WEPAY_ACH)? [] : [
- 'first_name' => 'required',
- 'last_name' => 'required',
- ];
-
- if ( !Input::get('sourceToken') && !(Input::get('plaidPublicToken') && Input::get('plaidAccountId'))) {
- $rules = array_merge(
- $rules,
- [
- 'card_number' => 'required',
- 'expiration_month' => 'required',
- 'expiration_year' => 'required',
- 'cvv' => 'required',
- ]
- );
- }
-
- $requireAddress = $accountGateway->show_address && $paymentType != PAYMENT_TYPE_STRIPE_ACH && $paymentType != PAYMENT_TYPE_BRAINTREE_PAYPAL && $paymentType != PAYMENT_TYPE_WEPAY_ACH;
-
- if ($requireAddress) {
- $rules = array_merge($rules, [
- 'address1' => 'required',
- 'city' => 'required',
- 'state' => 'required',
- 'postal_code' => 'required',
- 'country_id' => 'required',
- ]);
- }
-
- if ($onSite) {
- $validator = Validator::make(Input::all(), $rules);
-
- if ($validator->fails()) {
- return $validator;
- }
-
- if ($requireAddress && $accountGateway->update_address) {
- $client->address1 = trim(Input::get('address1'));
- $client->address2 = trim(Input::get('address2'));
- $client->city = trim(Input::get('city'));
- $client->state = trim(Input::get('state'));
- $client->postal_code = trim(Input::get('postal_code'));
- $client->country_id = Input::get('country_id');
- $client->save();
- }
- }
-
- return true;
- }
-
- public function do_payment($invitationKey, $onSite = true, $useToken = false, $sourceId = false)
- {
- $invitation = Invitation::with('invoice.invoice_items', 'invoice.client.currency', 'invoice.client.account.currency', 'invoice.client.account.account_gateways.gateway')->where('invitation_key', '=', $invitationKey)->firstOrFail();
- $invoice = $invitation->invoice;
- $client = $invoice->client;
- $account = $client->account;
- $paymentType = Session::get($invitation->id . 'payment_type');
- $accountGateway = $account->getGatewayByType($paymentType);
- $paymentMethod = null;
-
- if ($useToken) {
- if(!$sourceId) {
- Session::flash('error', trans('texts.no_payment_method_specified'));
- return Redirect::to('payment/' . $invitationKey)->withInput(Request::except('cvv'));
- } else {
- $customerReference = $client->getGatewayToken($accountGateway, $accountGatewayToken/* return parameter*/);
- $paymentMethod = PaymentMethod::scope($sourceId, $account->id, $accountGatewayToken->id)->firstOrFail();
- $sourceReference = $paymentMethod->source_reference;
-
- // What type of payment is this?
- if ($paymentMethod->payment_type_id == PAYMENT_TYPE_ACH) {
- if ($accountGateway->gateway_id == GATEWAY_STRIPE) {
- $paymentType = PAYMENT_TYPE_STRIPE_ACH;
- } elseif ($accountGateway->gateway_id == GATEWAY_WEPAY) {
- $paymentType = PAYMENT_TYPE_WEPAY_ACH;
- }
- } elseif ($paymentMethod->payment_type_id == PAYMENT_TYPE_ID_PAYPAL && $accountGateway->gateway_id == GATEWAY_BRAINTREE) {
- $paymentType = PAYMENT_TYPE_BRAINTREE_PAYPAL;
- } elseif ($accountGateway->gateway_id == GATEWAY_STRIPE) {
- $paymentType = PAYMENT_TYPE_STRIPE_CREDIT_CARD;
- } else {
- $paymentType = PAYMENT_TYPE_CREDIT_CARD;
- }
- }
- }
-
- if (($validator = static::processPaymentClientDetails($client, $accountGateway, $paymentType, $onSite)) !== true) {
- return Redirect::to('payment/'.$invitationKey)
- ->withErrors($validator)
- ->withInput(Request::except('cvv'));
- }
-
- try {
- // For offsite payments send the client's details on file
- // If we're using a token then we don't need to send any other data
- if (!$onSite || $useToken) {
- $data = false;
- } else {
- $data = Input::all();
- }
-
- $gateway = $this->paymentService->createGateway($accountGateway);
- $details = $this->paymentService->getPaymentDetails($invitation, $accountGateway, $data);
- $details['paymentType'] = $paymentType;
-
- // Check for authorization
- if (($paymentType == PAYMENT_TYPE_STRIPE_ACH || $paymentType == PAYMENT_TYPE_WEPAY_ACH) && !Input::get('authorize_ach')) {
- Session::flash('error', trans('texts.ach_authorization_required'));
- return Redirect::to('client/paymentmethods/add/' . $typeLink.'/'.$sourceToken)->withInput(Request::except('cvv'));
- }
- if ($paymentType == PAYMENT_TYPE_WEPAY_ACH && !Input::get('tos_agree')) {
- Session::flash('error', trans('texts.wepay_payment_tos_agree_required'));
- return Redirect::to('client/paymentmethods/add/' . $typeLink.'/'.$sourceToken)->withInput(Request::except('cvv'));
- }
-
- // check if we're creating/using a billing token
- $tokenBillingSupported = false;
- $sourceReferenceParam = 'token';
- if ($accountGateway->gateway_id == GATEWAY_STRIPE) {
- $tokenBillingSupported = true;
- $customerReferenceParam = 'customerReference';
- } elseif ($accountGateway->gateway_id == GATEWAY_BRAINTREE) {
- $tokenBillingSupported = true;
- $sourceReferenceParam = 'paymentMethodToken';
- $customerReferenceParam = 'customerId';
-
- $deviceData = Input::get('device_data');
- if (!$deviceData) {
- $deviceData = Session::get($invitation->id . 'device_data');
- }
-
- if($deviceData) {
- $details['device_data'] = $deviceData;
- }
- } elseif ($accountGateway->gateway_id == GATEWAY_WEPAY) {
- $tokenBillingSupported = true;
- $customerReferenceParam = false;
- }
-
- if ($tokenBillingSupported) {
- if ($useToken) {
- if ($customerReferenceParam) {
- $details[$customerReferenceParam] = $customerReference;
- }
- $details[$sourceReferenceParam] = $sourceReference;
- unset($details['card']);
- } elseif ($account->token_billing_type_id == TOKEN_BILLING_ALWAYS || Input::get('token_billing') || $paymentType == PAYMENT_TYPE_STRIPE_ACH || $paymentType == PAYMENT_TYPE_WEPAY_ACH) {
- $token = $this->paymentService->createToken($paymentType, $gateway, $details, $accountGateway, $client, $invitation->contact_id, $customerReference/* return parameter */, $paymentMethod/* return parameter */);
- if ($token) {
- $details[$sourceReferenceParam] = $token;
- if ($customerReferenceParam) {
- $details[$customerReferenceParam] = $customerReference;
- }
-
- if ($paymentType == PAYMENT_TYPE_STRIPE_ACH && empty(Input::get('plaidPublicToken')) ) {
- // The user needs to complete verification
- Session::flash('message', trans('texts.bank_account_verification_next_steps'));
- return Redirect::to('/client/paymentmethods');
- }
- } else {
- $this->error('Token-No-Ref', $this->paymentService->lastError, $accountGateway);
- return Redirect::to('payment/'.$invitationKey)->withInput(Request::except('cvv'));
- }
- }
- }
-
- $response = $this->paymentService->purchase($accountGateway, $details);
-
- if ($accountGateway->gateway_id == GATEWAY_EWAY) {
- $ref = $response->getData()['AccessCode'];
- } elseif ($accountGateway->gateway_id == GATEWAY_TWO_CHECKOUT) {
- $ref = $response->getData()['cart_order_id'];
- } elseif ($accountGateway->gateway_id == GATEWAY_PAYFAST) {
- $ref = $response->getData()['m_payment_id'];
- } elseif ($accountGateway->gateway_id == GATEWAY_GOCARDLESS) {
- $ref = $response->getData()['signature'];
- } elseif ($accountGateway->gateway_id == GATEWAY_CYBERSOURCE) {
- $ref = $response->getData()['transaction_uuid'];
- } else {
- $ref = $response->getTransactionReference();
- }
-
- if (!$ref) {
- $this->error('No-Ref', $response->getMessage(), $accountGateway);
-
- if ($onSite && $paymentType != PAYMENT_TYPE_BRAINTREE_PAYPAL && $paymentType != PAYMENT_TYPE_WEPAY_ACH) {
- return Redirect::to('payment/'.$invitationKey)
- ->withInput(Request::except('cvv'));
- } else {
- return Redirect::to('view/'.$invitationKey);
- }
- }
-
- if ($response->isSuccessful()) {
- $payment = $this->paymentService->createPayment($invitation, $accountGateway, $ref, null, $details, $paymentMethod, $response);
- Session::flash('message', trans('texts.applied_payment'));
-
- if ($account->account_key == NINJA_ACCOUNT_KEY) {
- Session::flash('trackEventCategory', '/account');
- Session::flash('trackEventAction', '/buy_pro_plan');
- Session::flash('trackEventAmount', $payment->amount);
- }
-
- return Redirect::to('view/'.$payment->invitation->invitation_key);
- } elseif ($response->isRedirect()) {
-
- $invitation->transaction_reference = $ref;
- $invitation->save();
- Session::put('transaction_reference', $ref);
- Session::save();
- $response->redirect();
- } else {
- $this->error('Unknown', $response->getMessage(), $accountGateway);
- if ($onSite && $paymentType != PAYMENT_TYPE_BRAINTREE_PAYPAL && $paymentType != PAYMENT_TYPE_WEPAY_ACH) {
- return Redirect::to('payment/'.$invitationKey)->withInput(Request::except('cvv'));
- } else {
- return Redirect::to('view/'.$invitationKey);
- }
- }
- } catch (\Exception $e) {
- $this->error('Uncaught', false, $accountGateway, $e);
- if ($onSite && $paymentType != PAYMENT_TYPE_BRAINTREE_PAYPAL && $paymentType != PAYMENT_TYPE_WEPAY_ACH) {
- return Redirect::to('payment/'.$invitationKey)->withInput(Request::except('cvv'));
- } else {
- return Redirect::to('view/'.$invitationKey);
- }
- }
- }
-
- public function offsite_payment()
- {
- $payerId = Request::query('PayerID');
- $token = Request::query('token');
-
- if (!$token) {
- $token = Session::pull('transaction_reference');
- }
- if (!$token) {
- return redirect(NINJA_WEB_URL);
- }
-
- $invitation = Invitation::with('invoice.client.currency', 'invoice.client.account.account_gateways.gateway')->where('transaction_reference', '=', $token)->firstOrFail();
- $invoice = $invitation->invoice;
- $client = $invoice->client;
- $account = $client->account;
-
- if ($payerId) {
- $paymentType = PAYMENT_TYPE_PAYPAL;
- } else {
- $paymentType = Session::get($invitation->id . 'payment_type');
- }
- if (!$paymentType) {
- $this->error('No-Payment-Type', false, false);
- return Redirect::to($invitation->getLink());
- }
- $accountGateway = $account->getGatewayByType($paymentType);
- $gateway = $this->paymentService->createGateway($accountGateway);
-
- // Check for Dwolla payment error
- if ($accountGateway->isGateway(GATEWAY_DWOLLA) && Input::get('error')) {
- $this->error('Dwolla', Input::get('error_description'), $accountGateway);
- return Redirect::to($invitation->getLink());
- }
-
- // PayFast transaction referencce
- if ($accountGateway->isGateway(GATEWAY_PAYFAST) && Request::has('pt')) {
- $token = Request::query('pt');
- }
-
- try {
- if ($accountGateway->isGateway(GATEWAY_CYBERSOURCE)) {
- if (Input::get('decision') == 'ACCEPT') {
- $payment = $this->paymentService->createPayment($invitation, $accountGateway, $token, $payerId);
- Session::flash('message', trans('texts.applied_payment'));
- } else {
- $message = Input::get('message') . ': ' . Input::get('invalid_fields');
- Session::flash('error', $message);
- }
- return Redirect::to($invitation->getLink());
- } elseif (method_exists($gateway, 'completePurchase')
- && !$accountGateway->isGateway(GATEWAY_TWO_CHECKOUT)
- && !$accountGateway->isGateway(GATEWAY_CHECKOUT_COM)) {
- $details = $this->paymentService->getPaymentDetails($invitation, $accountGateway, array());
-
- $response = $this->paymentService->completePurchase($gateway, $accountGateway, $details, $token);
-
- $ref = $response->getTransactionReference() ?: $token;
-
- if ($response->isCancelled()) {
- // do nothing
- } elseif ($response->isSuccessful()) {
- $payment = $this->paymentService->createPayment($invitation, $accountGateway, $ref, $payerId, $details, null, $purchaseResponse);
- Session::flash('message', trans('texts.applied_payment'));
- } else {
- $this->error('offsite', $response->getMessage(), $accountGateway);
- }
- return Redirect::to($invitation->getLink());
- } else {
- $payment = $this->paymentService->createPayment($invitation, $accountGateway, $token, $payerId);
- Session::flash('message', trans('texts.applied_payment'));
- return Redirect::to($invitation->getLink());
- }
- } catch (\Exception $e) {
- $this->error('Offsite-uncaught', false, $accountGateway, $e);
- return Redirect::to($invitation->getLink());
- }
- }
-
public function store(CreatePaymentRequest $request)
{
$input = $request->input();
@@ -760,245 +138,7 @@ class PaymentController extends BaseController
Session::flash('message', $message);
}
- return Redirect::to('payments');
+ return redirect()->to('payments');
}
- private function error($type, $error, $accountGateway = false, $exception = false)
- {
- $message = '';
- if ($accountGateway && $accountGateway->gateway) {
- $message = $accountGateway->gateway->name . ': ';
- }
- $message .= $error ?: trans('texts.payment_error');
-
- Session::flash('error', $message);
- Utils::logError("Payment Error [{$type}]: " . ($exception ? Utils::getErrorString($exception) : $message), 'PHP', true);
- }
-
- public function getBankInfo($routingNumber) {
- if (strlen($routingNumber) != 9 || !preg_match('/\d{9}/', $routingNumber)) {
- return response()->json([
- 'message' => 'Invalid routing number',
- ], 400);
- }
-
- $data = PaymentMethod::lookupBankData($routingNumber);
-
- if (is_string($data)) {
- return response()->json([
- 'message' => $data,
- ], 500);
- } elseif (!empty($data)) {
- return response()->json($data);
- }
-
- return response()->json([
- 'message' => 'Bank not found',
- ], 404);
- }
-
- public function handlePaymentWebhook($accountKey, $gatewayId)
- {
- $gatewayId = intval($gatewayId);
-
- $account = Account::where('accounts.account_key', '=', $accountKey)->first();
-
- if (!$account) {
- return response()->json([
- 'message' => 'Unknown account',
- ], 404);
- }
-
- $accountGateway = $account->getGatewayConfig(intval($gatewayId));
-
- if (!$accountGateway) {
- return response()->json([
- 'message' => 'Unknown gateway',
- ], 404);
- }
-
- switch($gatewayId) {
- case GATEWAY_STRIPE:
- return $this->handleStripeWebhook($accountGateway);
- case GATEWAY_WEPAY:
- return $this->handleWePayWebhook($accountGateway);
- default:
- return response()->json([
- 'message' => 'Unsupported gateway',
- ], 404);
- }
- }
-
- protected function handleWePayWebhook($accountGateway) {
- $data = Input::all();
- $accountId = $accountGateway->account_id;
-
- foreach (array_keys($data) as $key) {
- if ('_id' == substr($key, -3)) {
- $objectType = substr($key, 0, -3);
- $objectId = $data[$key];
- break;
- }
- }
-
- if (!isset($objectType)) {
- return response()->json([
- 'message' => 'Could not find object id parameter',
- ], 400);
- }
-
- if ($objectType == 'credit_card') {
- $paymentMethod = PaymentMethod::scope(false, $accountId)->where('source_reference', '=', $objectId)->first();
-
- if (!$paymentMethod) {
- return array('message' => 'Unknown payment method');
- }
-
- $wepay = \Utils::setupWePay($accountGateway);
- $source = $wepay->request('credit_card', array(
- 'client_id' => WEPAY_CLIENT_ID,
- 'client_secret' => WEPAY_CLIENT_SECRET,
- 'credit_card_id' => intval($objectId),
- ));
-
- if ($source->state == 'deleted') {
- $paymentMethod->delete();
- } else {
- $this->paymentService->convertPaymentMethodFromWePay($source, null, $paymentMethod)->save();
- }
-
- return array('message' => 'Processed successfully');
- } elseif ($objectType == 'account') {
- $config = $accountGateway->getConfig();
- if ($config->accountId != $objectId) {
- return array('message' => 'Unknown account');
- }
-
- $wepay = \Utils::setupWePay($accountGateway);
- $wepayAccount = $wepay->request('account', array(
- 'account_id' => intval($objectId),
- ));
-
- if ($wepayAccount->state == 'deleted') {
- $accountGateway->delete();
- } else {
- $config->state = $wepayAccount->state;
- $accountGateway->setConfig($config);
- $accountGateway->save();
- }
-
- return array('message' => 'Processed successfully');
- } elseif ($objectType == 'checkout') {
- $payment = Payment::scope(false, $accountId)->where('transaction_reference', '=', $objectId)->first();
-
- if (!$payment) {
- return array('message' => 'Unknown payment');
- }
-
- $wepay = \Utils::setupWePay($accountGateway);
- $checkout = $wepay->request('checkout', array(
- 'checkout_id' => intval($objectId),
- ));
-
- if ($checkout->state == 'refunded') {
- $payment->recordRefund();
- } elseif (!empty($checkout->refund) && !empty($checkout->refund->amount_refunded) && ($checkout->refund->amount_refunded - $payment->refunded) > 0) {
- $payment->recordRefund($checkout->refund->amount_refunded - $payment->refunded);
- }
-
- if ($checkout->state == 'captured') {
- $payment->markComplete();
- } elseif ($checkout->state == 'cancelled') {
- $payment->markCancelled();
- } elseif ($checkout->state == 'failed') {
- $payment->markFailed();
- }
-
- return array('message' => 'Processed successfully');
- } else {
- return array('message' => 'Ignoring event');
- }
- }
-
- protected function handleStripeWebhook($accountGateway) {
- $eventId = Input::get('id');
- $eventType= Input::get('type');
- $accountId = $accountGateway->account_id;
-
- if (!$eventId) {
- return response()->json(['message' => 'Missing event id'], 400);
- }
-
- if (!$eventType) {
- return response()->json(['message' => 'Missing event type'], 400);
- }
-
- $supportedEvents = array(
- 'charge.failed',
- 'charge.succeeded',
- 'customer.source.updated',
- 'customer.source.deleted',
- );
-
- if (!in_array($eventType, $supportedEvents)) {
- return array('message' => 'Ignoring event');
- }
-
- // Fetch the event directly from Stripe for security
- $eventDetails = $this->paymentService->makeStripeCall($accountGateway, 'GET', 'events/'.$eventId);
-
- if (is_string($eventDetails) || !$eventDetails) {
- return response()->json([
- 'message' => $eventDetails ? $eventDetails : 'Could not get event details.',
- ], 500);
- }
-
- if ($eventType != $eventDetails['type']) {
- return response()->json(['message' => 'Event type mismatch'], 400);
- }
-
- if (!$eventDetails['pending_webhooks']) {
- return response()->json(['message' => 'This is not a pending event'], 400);
- }
-
-
- if ($eventType == 'charge.failed' || $eventType == 'charge.succeeded') {
- $charge = $eventDetails['data']['object'];
- $transactionRef = $charge['id'];
-
- $payment = Payment::scope(false, $accountId)->where('transaction_reference', '=', $transactionRef)->first();
-
- if (!$payment) {
- return array('message' => 'Unknown payment');
- }
-
- if ($eventType == 'charge.failed') {
- if (!$payment->isFailed()) {
- $payment->markFailed($charge['failure_message']);
- $this->userMailer->sendNotification($payment->user, $payment->invoice, 'payment_failed', $payment);
- }
- } elseif ($eventType == 'charge.succeeded') {
- $payment->markComplete();
- } elseif ($eventType == 'charge.refunded') {
- $payment->recordRefund($charge['amount_refunded'] / 100 - $payment->refunded);
- }
- } elseif($eventType == 'customer.source.updated' || $eventType == 'customer.source.deleted') {
- $source = $eventDetails['data']['object'];
- $sourceRef = $source['id'];
-
- $paymentMethod = PaymentMethod::scope(false, $accountId)->where('source_reference', '=', $sourceRef)->first();
-
- if (!$paymentMethod) {
- return array('message' => 'Unknown payment method');
- }
-
- if ($eventType == 'customer.source.deleted') {
- $paymentMethod->delete();
- } elseif ($eventType == 'customer.source.updated') {
- $this->paymentService->convertPaymentMethodFromStripe($source, null, $paymentMethod)->save();
- }
- }
-
- return array('message' => 'Processed successfully');
- }
}
diff --git a/app/Http/Requests/CreateOnlinePaymentRequest.php b/app/Http/Requests/CreateOnlinePaymentRequest.php
new file mode 100644
index 000000000000..815c8f5b217d
--- /dev/null
+++ b/app/Http/Requests/CreateOnlinePaymentRequest.php
@@ -0,0 +1,46 @@
+invitation->account;
+
+ $paymentDriver = $account->paymentDriver($this->invitation, $this->gateway_type);
+
+ return $paymentDriver->rules();
+ }
+
+ public function sanitize()
+ {
+ $input = $this->all();
+
+ $invitation = Invitation::with('invoice.invoice_items', 'invoice.client.currency', 'invoice.client.account.currency', 'invoice.client.account.account_gateways.gateway')
+ ->where('invitation_key', '=', $this->invitation_key)
+ ->firstOrFail();
+
+ $input['invitation'] = $invitation;
+ $input['gateway_type'] = session($invitation->id . 'gateway_type');
+
+ $this->replace($input);
+
+ return $this->all();
+ }
+}
diff --git a/app/Http/routes.php b/app/Http/routes.php
index ab1bd91d3273..4413174f5ec3 100644
--- a/app/Http/routes.php
+++ b/app/Http/routes.php
@@ -41,15 +41,16 @@ Route::group(['middleware' => 'auth:client'], function() {
Route::get('download/{invitation_key}', 'ClientPortalController@download');
Route::get('view', 'HomeController@viewLogo');
Route::get('approve/{invitation_key}', 'QuoteController@approve');
- Route::get('payment/{invitation_key}/{payment_type?}/{source_id?}', 'PaymentController@show_payment');
- Route::post('payment/{invitation_key}', 'PaymentController@do_payment');
- Route::match(['GET', 'POST'], 'complete', 'PaymentController@offsite_payment');
- Route::get('client/paymentmethods', 'ClientPortalController@paymentMethods');
- Route::post('client/paymentmethods/verify', 'ClientPortalController@verifyPaymentMethod');
- Route::get('client/paymentmethods/add/{payment_type}/{source_id?}', 'ClientPortalController@addPaymentMethod');
- Route::post('client/paymentmethods/add/{payment_type}', 'ClientPortalController@postAddPaymentMethod');
- Route::post('client/paymentmethods/default', 'ClientPortalController@setDefaultPaymentMethod');
- Route::post('client/paymentmethods/{source_id}/remove', 'ClientPortalController@removePaymentMethod');
+ Route::get('payment/{invitation_key}/{gateway_type?}/{source_id?}', 'OnlinePaymentController@showPayment');
+ Route::post('payment/{invitation_key}', 'OnlinePaymentController@doPayment');
+ Route::match(['GET', 'POST'], 'complete/{invitation_key?}/{gateway_type?}', 'OnlinePaymentController@offsitePayment');
+ Route::get('bank/{routing_number}', 'OnlinePaymentController@getBankInfo');
+ Route::get('client/payment_methods', 'ClientPortalController@paymentMethods');
+ Route::post('client/payment_methods/verify', 'ClientPortalController@verifyPaymentMethod');
+ //Route::get('client/payment_methods/add/{gateway_type}/{source_id?}', 'ClientPortalController@addPaymentMethod');
+ //Route::post('client/payment_methods/add/{gateway_type}', 'ClientPortalController@postAddPaymentMethod');
+ Route::post('client/payment_methods/default', 'ClientPortalController@setDefaultPaymentMethod');
+ Route::post('client/payment_methods/{source_id}/remove', 'ClientPortalController@removePaymentMethod');
Route::get('client/quotes', 'ClientPortalController@quoteIndex');
Route::get('client/invoices', 'ClientPortalController@invoiceIndex');
Route::get('client/invoices/recurring', 'ClientPortalController@recurringInvoiceIndex');
@@ -71,11 +72,10 @@ Route::group(['middleware' => 'auth:client'], function() {
});
-Route::get('bank/{routing_number}', 'PaymentController@getBankInfo');
Route::post('paymenthook/{accountKey}/{gatewayId}', 'PaymentController@handlePaymentWebhook');
-Route::get('license', 'PaymentController@show_license_payment');
-Route::post('license', 'PaymentController@do_license_payment');
-Route::get('claim_license', 'PaymentController@claim_license');
+Route::get('license', 'NinjaController@show_license_payment');
+Route::post('license', 'NinjaController@do_license_payment');
+Route::get('claim_license', 'NinjaController@claim_license');
Route::post('signup/validate', 'AccountController@checkEmail');
Route::post('signup/submit', 'AccountController@submitSignup');
@@ -557,7 +557,6 @@ if (!defined('CONTACT_EMAIL')) {
define('PAYMENT_LIBRARY_PHP_PAYMENTS', 2);
define('GATEWAY_AUTHORIZE_NET', 1);
- define('GATEWAY_AUTHORIZE_NET_SIM', 2);
define('GATEWAY_EWAY', 4);
define('GATEWAY_MOLLIE', 9);
define('GATEWAY_PAYFAST', 13);
@@ -661,7 +660,7 @@ if (!defined('CONTACT_EMAIL')) {
define('PAYMENT_TYPE_EUROCARD', 11);
define('PAYMENT_TYPE_NOVA', 12);
define('PAYMENT_TYPE_CREDIT_CARD_OTHER', 13);
- define('PAYMENT_TYPE_ID_PAYPAL', 14);
+ define('PAYMENT_TYPE_PAYPAL', 14);
define('PAYMENT_TYPE_CARTE_BLANCHE', 17);
define('PAYMENT_TYPE_UNIONPAY', 18);
define('PAYMENT_TYPE_JCB', 19);
@@ -674,18 +673,12 @@ if (!defined('CONTACT_EMAIL')) {
define('PAYMENT_METHOD_STATUS_VERIFICATION_FAILED', 'verification_failed');
define('PAYMENT_METHOD_STATUS_VERIFIED', 'verified');
- define('PAYMENT_TYPE_PAYPAL', 'PAYMENT_TYPE_PAYPAL');
- define('PAYMENT_TYPE_STRIPE', 'PAYMENT_TYPE_STRIPE');
- define('PAYMENT_TYPE_STRIPE_CREDIT_CARD', 'PAYMENT_TYPE_STRIPE_CREDIT_CARD');
- define('PAYMENT_TYPE_STRIPE_ACH', 'PAYMENT_TYPE_STRIPE_ACH');
- define('PAYMENT_TYPE_BRAINTREE_PAYPAL', 'PAYMENT_TYPE_BRAINTREE_PAYPAL');
- define('PAYMENT_TYPE_WEPAY_ACH', 'PAYMENT_TYPE_WEPAY_ACH');
- define('PAYMENT_TYPE_CREDIT_CARD', 'PAYMENT_TYPE_CREDIT_CARD');
- define('PAYMENT_TYPE_DIRECT_DEBIT', 'PAYMENT_TYPE_DIRECT_DEBIT');
- define('PAYMENT_TYPE_BITCOIN', 'PAYMENT_TYPE_BITCOIN');
- define('PAYMENT_TYPE_DWOLLA', 'PAYMENT_TYPE_DWOLLA');
- define('PAYMENT_TYPE_TOKEN', 'PAYMENT_TYPE_TOKEN');
- define('PAYMENT_TYPE_ANY', 'PAYMENT_TYPE_ANY');
+ define('GATEWAY_TYPE_CREDIT_CARD', 'credit_card');
+ define('GATEWAY_TYPE_BANK_TRANSFER', 'bank_transfer');
+ define('GATEWAY_TYPE_PAYPAL', 'paypal');
+ define('GATEWAY_TYPE_BITCOIN', 'bitcoin');
+ define('GATEWAY_TYPE_DWOLLA', 'dwolla');
+ define('GATEWAY_TYPE_TOKEN', 'token');
define('REMINDER1', 'reminder1');
define('REMINDER2', 'reminder2');
diff --git a/app/Listeners/CreditListener.php b/app/Listeners/CreditListener.php
index aad2dd4d65b8..fe8e174ab3ed 100644
--- a/app/Listeners/CreditListener.php
+++ b/app/Listeners/CreditListener.php
@@ -19,7 +19,7 @@ class CreditListener
public function deletedPayment(PaymentWasDeleted $event)
{
$payment = $event->payment;
-
+
// if the payment was from a credit we need to refund the credit
if ($payment->payment_type_id != PAYMENT_TYPE_CREDIT) {
return;
@@ -28,7 +28,7 @@ class CreditListener
$credit = Credit::createNew();
$credit->client_id = $payment->client_id;
$credit->credit_date = Carbon::now()->toDateTimeString();
- $credit->balance = $credit->amount = $payment->amount - $payment->refunded;
+ $credit->balance = $credit->amount = $payment->getCompletedAmount();
$credit->private_notes = $payment->transaction_reference;
$credit->save();
}
@@ -36,7 +36,7 @@ class CreditListener
public function refundedPayment(PaymentWasRefunded $event)
{
$payment = $event->payment;
-
+
// if the payment was from a credit we need to refund the credit
if ($payment->payment_type_id != PAYMENT_TYPE_CREDIT) {
return;
diff --git a/app/Listeners/InvoiceListener.php b/app/Listeners/InvoiceListener.php
index a797c418ef25..d5d20ae991c6 100644
--- a/app/Listeners/InvoiceListener.php
+++ b/app/Listeners/InvoiceListener.php
@@ -61,7 +61,7 @@ class InvoiceListener
{
$payment = $event->payment;
$invoice = $payment->invoice;
- $adjustment = $payment->amount - $payment->refunded;
+ $adjustment = $payment->getCompletedAmount();
$invoice->updateBalances($adjustment);
$invoice->updatePaidStatus();
@@ -91,7 +91,7 @@ class InvoiceListener
{
$payment = $event->payment;
$invoice = $payment->invoice;
- $adjustment = $payment->amount - $payment->refunded;
+ $adjustment = $payment->getCompletedAmount();
$invoice->updateBalances($adjustment);
$invoice->updatePaidStatus();
@@ -105,7 +105,7 @@ class InvoiceListener
$payment = $event->payment;
$invoice = $payment->invoice;
- $adjustment = ($payment->amount - $payment->refunded) * -1;
+ $adjustment = $payment->getCompletedAmount() * -1;
$invoice->updateBalances($adjustment);
$invoice->updatePaidStatus();
diff --git a/app/Models/Account.php b/app/Models/Account.php
index 7df9218438bf..e1c849ed80aa 100644
--- a/app/Models/Account.php
+++ b/app/Models/Account.php
@@ -384,37 +384,44 @@ class Account extends Eloquent
return $format;
}
- public function getGatewayByType($type = PAYMENT_TYPE_ANY, $exceptFor = null)
+ /*
+ public function defaultGatewayType()
{
- if ($type == PAYMENT_TYPE_STRIPE_ACH || $type == PAYMENT_TYPE_STRIPE_CREDIT_CARD) {
- $type = PAYMENT_TYPE_STRIPE;
+ $accountGateway = $this->account_gateways[0];
+ $paymentDriver = $accountGateway->paymentDriver();
+
+ return $paymentDriver->gatewayTypes()[0];
+ }
+ */
+
+ public function getGatewayByType($type = false)
+ {
+ if ( ! $this->relationLoaded('account_gateways')) {
+ $this->load('account_gateways');
}
- if ($type == PAYMENT_TYPE_WEPAY_ACH) {
- return $this->getGatewayConfig(GATEWAY_WEPAY);
- }
-
- foreach ($this->account_gateways as $gateway) {
- if ($exceptFor && ($gateway->id == $exceptFor->id)) {
- continue;
+ foreach ($this->account_gateways as $accountGateway) {
+ if ( ! $type) {
+ return $accountGateway;
}
- if (!$type || $type == PAYMENT_TYPE_ANY) {
- return $gateway;
- } elseif ($gateway->isPaymentType($type)) {
- return $gateway;
- } elseif ($type == PAYMENT_TYPE_CREDIT_CARD && $gateway->isPaymentType(PAYMENT_TYPE_STRIPE)) {
- return $gateway;
- } elseif ($type == PAYMENT_TYPE_DIRECT_DEBIT && $gateway->getAchEnabled()) {
- return $gateway;
- } elseif ($type == PAYMENT_TYPE_PAYPAL && $gateway->getPayPalEnabled()) {
- return $gateway;
+ $paymentDriver = $accountGateway->paymentDriver();
+
+ if ($paymentDriver->handles($type)) {
+ return $accountGateway;
}
}
return false;
}
+ public function paymentDriver($invitation = false, $gatewayType = false)
+ {
+ $accountGateway = $this->getGatewayByType($gatewayType);
+
+ return $accountGateway->paymentDriver($invitation, $gatewayType);
+ }
+
public function gatewayIds()
{
return $this->account_gateways()->pluck('gateway_id')->toArray();
@@ -1437,18 +1444,6 @@ class Account extends Eloquent
public function getFontFolders(){
return array_map(function($item){return $item['folder'];}, $this->getFontsData());
}
-
- public function canAddGateway($type){
- if ($type == PAYMENT_TYPE_STRIPE) {
- $type == PAYMENT_TYPE_CREDIT_CARD;
- }
-
- if($this->getGatewayByType($type)) {
- return false;
- }
-
- return true;
- }
}
Account::updated(function ($account)
diff --git a/app/Models/AccountGateway.php b/app/Models/AccountGateway.php
index 563fe16720fb..fec6cc722c9c 100644
--- a/app/Models/AccountGateway.php
+++ b/app/Models/AccountGateway.php
@@ -3,10 +3,14 @@
use Crypt;
use App\Models\Gateway;
use Illuminate\Database\Eloquent\SoftDeletes;
+use Laracasts\Presenter\PresentableTrait;
class AccountGateway extends EntityModel
{
use SoftDeletes;
+ use PresentableTrait;
+
+ protected $presenter = 'App\Ninja\Presenters\AccountGatewayPresenter';
protected $dates = ['deleted_at'];
public function getEntityType()
@@ -33,14 +37,18 @@ class AccountGateway extends EntityModel
return $arrayOfImages;
}
- public function getPaymentType()
+ public function paymentDriver($invitation = false, $gatewayType = false)
{
- return Gateway::getPaymentType($this->gateway_id);
- }
-
- public function isPaymentType($type)
- {
- return $this->getPaymentType() == $type;
+ $folder = "App\\Ninja\\PaymentDrivers\\";
+ $class = $folder . $this->gateway->provider . 'PaymentDriver';
+ $class = str_replace('_', '', $class);
+
+ if (class_exists($class)) {
+ return new $class($this, $invitation, $gatewayType);
+ } else {
+ $baseClass = $folder . "BasePaymentDriver";
+ return new $baseClass($this, $invitation, $gatewayType);
+ }
}
public function isGateway($gatewayId)
@@ -131,4 +139,3 @@ class AccountGateway extends EntityModel
return \URL::to(env('WEBHOOK_PREFIX','').'paymenthook/'.$account->account_key.'/'.$this->gateway_id.env('WEBHOOK_SUFFIX',''));
}
}
-
diff --git a/app/Models/AccountGatewayToken.php b/app/Models/AccountGatewayToken.php
index 06494325662e..363bd26e86a2 100644
--- a/app/Models/AccountGatewayToken.php
+++ b/app/Models/AccountGatewayToken.php
@@ -9,17 +9,54 @@ class AccountGatewayToken extends Eloquent
protected $dates = ['deleted_at'];
public $timestamps = true;
- protected $casts = [
- 'uses_local_payment_methods' => 'boolean',
- ];
+ protected $casts = [];
public function payment_methods()
{
return $this->hasMany('App\Models\PaymentMethod');
}
+ public function account_gateway()
+ {
+ return $this->belongsTo('App\Models\AccountGateway');
+ }
+
public function default_payment_method()
{
return $this->hasOne('App\Models\PaymentMethod', 'id', 'default_payment_method_id');
}
-}
\ No newline at end of file
+
+ public function autoBillLater()
+ {
+ return $this->default_payment_method->requiresDelayedAutoBill();
+ }
+
+ public function scopeClientAndGateway($query, $clientId, $accountGatewayId)
+ {
+ $query->where('client_id', '=', $clientId)
+ ->where('account_gateway_id', '=', $accountGatewayId);
+
+ return $query;
+ }
+
+ public function gatewayName()
+ {
+ return $this->account_gateway->gateway->name;
+ }
+
+ public function gatewayLink()
+ {
+ $accountGateway = $this->account_gateway;
+
+ if ($accountGateway->gateway_id == GATEWAY_STRIPE) {
+ return "https://dashboard.stripe.com/customers/{$this->token}";
+ } elseif ($accountGateway->gateway_id == GATEWAY_BRAINTREE) {
+ $merchantId = $accountGateway->getConfig()->merchantId;
+ $testMode = $accountGateway->getConfig()->testMode;
+ return $testMode ? "https://sandbox.braintreegateway.com/merchants/{$merchantId}/customers/{$this->token}" : "https://www.braintreegateway.com/merchants/{$merchantId}/customers/{$this->token}";
+ } else {
+ return false;
+ }
+ }
+
+}
diff --git a/app/Models/Client.php b/app/Models/Client.php
index 0198d46c200d..e573b5feba2c 100644
--- a/app/Models/Client.php
+++ b/app/Models/Client.php
@@ -5,6 +5,7 @@ use DB;
use Carbon;
use Laracasts\Presenter\PresentableTrait;
use Illuminate\Database\Eloquent\SoftDeletes;
+use App\Models\AccountGatewayToken;
class Client extends EntityModel
{
@@ -154,7 +155,7 @@ class Client extends EntityModel
$contact = Contact::createNew();
$contact->send_invoice = true;
}
-
+
if (Utils::hasFeature(FEATURE_CLIENT_PORTAL_PASSWORD) && $this->account->enable_portal_password){
if(!empty($data['password']) && $data['password']!='-%unchanged%-'){
$contact->password = bcrypt($data['password']);
@@ -162,7 +163,7 @@ class Client extends EntityModel
$contact->password = null;
}
}
-
+
$contact->fill($data);
$contact->is_primary = $isPrimary;
@@ -177,7 +178,7 @@ class Client extends EntityModel
$this->balance = $this->balance + $balanceAdjustment;
$this->paid_to_date = $this->paid_to_date + $paidToDateAdjustment;
-
+
$this->save();
}
@@ -198,20 +199,20 @@ class Client extends EntityModel
{
return $this->name;
}
-
+
public function getPrimaryContact()
{
return $this->contacts()
->whereIsPrimary(true)
->first();
}
-
+
public function getDisplayName()
{
if ($this->name) {
return $this->name;
}
-
+
if ( ! count($this->contacts)) {
return '';
}
@@ -260,49 +261,28 @@ class Client extends EntityModel
}
}
-
- public function getGatewayToken(&$accountGateway = null, &$token = null)
+ public function getGatewayToken()
{
- $account = $this->account;
-
- if ( ! $account->relationLoaded('account_gateways')) {
- $account->load('account_gateways');
- }
+ $accountGateway = $this->account->getGatewayByType(GATEWAY_TYPE_TOKEN);
- if (!count($account->account_gateways)) {
- return false;
- }
-
- if (!$accountGateway){
- $accountGateway = $account->getTokenGateway();
- }
-
- if (!$accountGateway) {
+ if ( ! $accountGateway) {
return false;
}
- $token = AccountGatewayToken::where('client_id', '=', $this->id)
- ->where('account_gateway_id', '=', $accountGateway->id)->first();
-
- return $token ? $token->token : false;
+ return AccountGatewayToken::clientAndGateway($this->id, $accountGateway->id)->first();
}
- public function getGatewayLink(&$accountGateway = null)
+ public function autoBillLater()
{
- $token = $this->getGatewayToken($accountGateway);
- if (!$token) {
- return false;
+ if ($token = $this->getGatewayToken()) {
+ if ($this->account->auto_bill_on_due_date) {
+ return true;
+ }
+
+ return $token->autoBillLater();
}
- if ($accountGateway->gateway_id == GATEWAY_STRIPE) {
- return "https://dashboard.stripe.com/customers/{$token}";
- } elseif ($accountGateway->gateway_id == GATEWAY_BRAINTREE) {
- $merchantId = $accountGateway->getConfig()->merchantId;
- $testMode = $accountGateway->getConfig()->testMode;
- return $testMode ? "https://sandbox.braintreegateway.com/merchants/{$merchantId}/customers/{$token}" : "https://www.braintreegateway.com/merchants/{$merchantId}/customers/{$token}";
- } else {
- return false;
- }
+ return false;
}
public function getAmount()
@@ -323,6 +303,19 @@ class Client extends EntityModel
return $this->account->currency_id ?: DEFAULT_CURRENCY;
}
+ public function getCurrencyCode()
+ {
+ if ($this->currency) {
+ return $this->currency->code;
+ }
+
+ if (!$this->account) {
+ $this->load('account');
+ }
+
+ return $this->account->currency ? $this->account->currency->code : 'USD';
+ }
+
public function getCounter($isQuote)
{
return $isQuote ? $this->quote_number_counter : $this->invoice_number_counter;
diff --git a/app/Models/Gateway.php b/app/Models/Gateway.php
index 6df8af5e5f33..57064bb73f91 100644
--- a/app/Models/Gateway.php
+++ b/app/Models/Gateway.php
@@ -8,6 +8,15 @@ class Gateway extends Eloquent
{
public $timestamps = true;
+ public static $gatewayTypes = [
+ GATEWAY_TYPE_CREDIT_CARD,
+ GATEWAY_TYPE_BANK_TRANSFER,
+ GATEWAY_TYPE_PAYPAL,
+ GATEWAY_TYPE_BITCOIN,
+ GATEWAY_TYPE_DWOLLA,
+ GATEWAY_TYPE_TOKEN,
+ ];
+
// these will appear in the primary gateway select
// the rest are shown when selecting 'more options'
public static $preferred = [
@@ -26,16 +35,6 @@ class Gateway extends Eloquent
GATEWAY_DWOLLA,
];
- // TODO remove this
- public static $paymentTypes = [
- PAYMENT_TYPE_STRIPE,
- PAYMENT_TYPE_CREDIT_CARD,
- PAYMENT_TYPE_PAYPAL,
- PAYMENT_TYPE_BITCOIN,
- PAYMENT_TYPE_DIRECT_DEBIT,
- PAYMENT_TYPE_DWOLLA,
- ];
-
public static $hiddenFields = [
// PayPal
'headerImageUrl',
@@ -103,21 +102,11 @@ class Gateway extends Eloquent
}
}
- /*
- public static function getPaymentTypeLinks() {
- $data = [];
- foreach (self::$paymentTypes as $type) {
- $data[] = Utils::toCamelCase(strtolower(str_replace('PAYMENT_TYPE_', '', $type)));
- }
- return $data;
- }
- */
-
public function getHelp()
{
$link = '';
- if ($this->id == GATEWAY_AUTHORIZE_NET || $this->id == GATEWAY_AUTHORIZE_NET_SIM) {
+ if ($this->id == GATEWAY_AUTHORIZE_NET) {
$link = 'http://reseller.authorize.net/application/?id=5560364';
} elseif ($this->id == GATEWAY_PAYPAL_EXPRESS) {
$link = 'https://www.paypal.com/us/cgi-bin/webscr?cmd=_login-api-run';
@@ -141,24 +130,4 @@ class Gateway extends Eloquent
{
return Omnipay::create($this->provider)->getDefaultParameters();
}
-
- public static function getPaymentType($gatewayId) {
- if ($gatewayId == GATEWAY_PAYPAL_EXPRESS) {
- return PAYMENT_TYPE_PAYPAL;
- } else if ($gatewayId == GATEWAY_BITPAY) {
- return PAYMENT_TYPE_BITCOIN;
- } else if ($gatewayId == GATEWAY_DWOLLA) {
- return PAYMENT_TYPE_DWOLLA;
- } else if ($gatewayId == GATEWAY_GOCARDLESS) {
- return PAYMENT_TYPE_DIRECT_DEBIT;
- } else if ($gatewayId == GATEWAY_STRIPE) {
- return PAYMENT_TYPE_STRIPE;
- } else {
- return PAYMENT_TYPE_CREDIT_CARD;
- }
- }
-
- public static function getPrettyPaymentType($gatewayId) {
- return trans('texts.' . strtolower(Gateway::getPaymentType($gatewayId)));
- }
}
diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php
index fad5832da867..5a645b333067 100644
--- a/app/Models/Invoice.php
+++ b/app/Models/Invoice.php
@@ -890,13 +890,13 @@ class Invoice extends EntityModel implements BalanceAffecting
if ($this->tax_name1) {
$invoiceTaxAmount = round($taxable * ($this->tax_rate1 / 100), 2);
- $invoicePaidAmount = $this->amount && $invoiceTaxAmount ? ($paidAmount / $this->amount * $invoiceTaxAmount) : 0;
+ $invoicePaidAmount = floatVal($this->amount) && $invoiceTaxAmount ? ($paidAmount / $this->amount * $invoiceTaxAmount) : 0;
$this->calculateTax($taxes, $this->tax_name1, $this->tax_rate1, $invoiceTaxAmount, $invoicePaidAmount);
}
if ($this->tax_name2) {
$invoiceTaxAmount = round($taxable * ($this->tax_rate2 / 100), 2);
- $invoicePaidAmount = $this->amount && $invoiceTaxAmount ? ($paidAmount / $this->amount * $invoiceTaxAmount) : 0;
+ $invoicePaidAmount = floatVal($this->amount) && $invoiceTaxAmount ? ($paidAmount / $this->amount * $invoiceTaxAmount) : 0;
$this->calculateTax($taxes, $this->tax_name2, $this->tax_rate2, $invoiceTaxAmount, $invoicePaidAmount);
}
@@ -905,13 +905,13 @@ class Invoice extends EntityModel implements BalanceAffecting
if ($invoiceItem->tax_name1) {
$itemTaxAmount = round($taxable * ($invoiceItem->tax_rate1 / 100), 2);
- $itemPaidAmount = $this->amount && $itemTaxAmount ? ($paidAmount / $this->amount * $itemTaxAmount) : 0;
+ $itemPaidAmount = floatVal($this->amount) && $itemTaxAmount ? ($paidAmount / $this->amount * $itemTaxAmount) : 0;
$this->calculateTax($taxes, $invoiceItem->tax_name1, $invoiceItem->tax_rate1, $itemTaxAmount, $itemPaidAmount);
}
if ($invoiceItem->tax_name2) {
$itemTaxAmount = round($taxable * ($invoiceItem->tax_rate2 / 100), 2);
- $itemPaidAmount = $this->amount && $itemTaxAmount ? ($paidAmount / $this->amount * $itemTaxAmount) : 0;
+ $itemPaidAmount = floatVal($this->amount) && $itemTaxAmount ? ($paidAmount / $this->amount * $itemTaxAmount) : 0;
$this->calculateTax($taxes, $invoiceItem->tax_name2, $invoiceItem->tax_rate2, $itemTaxAmount, $itemPaidAmount);
}
}
diff --git a/app/Models/Payment.php b/app/Models/Payment.php
index 288bc566a8f1..a3f094315782 100644
--- a/app/Models/Payment.php
+++ b/app/Models/Payment.php
@@ -118,33 +118,41 @@ class Payment extends EntityModel
public function recordRefund($amount = null)
{
- if (!$this->isRefunded() && !$this->isVoided()) {
- if (!$amount) {
- $amount = $this->amount;
- }
-
- $new_refund = min($this->amount, $this->refunded + $amount);
- $refund_change = $new_refund - $this->refunded;
-
- if ($refund_change) {
- $this->refunded = $new_refund;
- $this->payment_status_id = $this->refunded == $this->amount ? PAYMENT_STATUS_REFUNDED : PAYMENT_STATUS_PARTIALLY_REFUNDED;
- $this->save();
-
- Event::fire(new PaymentWasRefunded($this, $refund_change));
- }
+ if ($this->isRefunded() || $this->isVoided()) {
+ return false;
}
+
+ if (!$amount) {
+ $amount = $this->amount;
+ }
+
+ $new_refund = min($this->amount, $this->refunded + $amount);
+ $refund_change = $new_refund - $this->refunded;
+
+ if ($refund_change) {
+ $this->refunded = $new_refund;
+ $this->payment_status_id = $this->refunded == $this->amount ? PAYMENT_STATUS_REFUNDED : PAYMENT_STATUS_PARTIALLY_REFUNDED;
+ $this->save();
+
+ Event::fire(new PaymentWasRefunded($this, $refund_change));
+ }
+
+ return true;
}
public function markVoided()
{
- if (!$this->isVoided() && !$this->isPartiallyRefunded() && !$this->isRefunded()) {
- $this->refunded = $this->amount;
- $this->payment_status_id = PAYMENT_STATUS_VOIDED;
- $this->save();
-
- Event::fire(new PaymentWasVoided($this));
+ if ($this->isVoided() || $this->isPartiallyRefunded() || $this->isRefunded()) {
+ return false;
}
+
+ $this->refunded = $this->amount;
+ $this->payment_status_id = PAYMENT_STATUS_VOIDED;
+ $this->save();
+
+ Event::fire(new PaymentWasVoided($this));
+
+ return true;
}
public function markComplete()
diff --git a/app/Models/PaymentMethod.php b/app/Models/PaymentMethod.php
index 8c3ea8b2dfd8..51b919946ff1 100644
--- a/app/Models/PaymentMethod.php
+++ b/app/Models/PaymentMethod.php
@@ -8,31 +8,11 @@ class PaymentMethod extends EntityModel
{
use SoftDeletes;
- protected $dates = ['deleted_at'];
public $timestamps = true;
+
+ protected $dates = ['deleted_at'];
protected $hidden = ['id'];
- public static function createNew($accountGatewayToken = null)
- {
- $entity = new PaymentMethod();
-
- $entity->account_id = $accountGatewayToken->account_id;
- $entity->account_gateway_token_id = $accountGatewayToken->id;
-
- $lastEntity = static::scope(false, $entity->account_id);
-
- $lastEntity = $lastEntity->orderBy('public_id', 'DESC')
- ->first();
-
- if ($lastEntity) {
- $entity->public_id = $lastEntity->public_id + 1;
- } else {
- $entity->public_id = 1;
- }
-
- return $entity;
- }
-
public function account()
{
return $this->belongsTo('App\Models\Account');
@@ -86,15 +66,25 @@ class PaymentMethod extends EntityModel
return $value ? str_pad($value, 4, '0', STR_PAD_LEFT) : null;
}
- public function scopeScope($query, $publicId = false, $accountId = false, $accountGatewayTokenId = false)
+ public function scopeClientId($query, $clientId)
{
- $query = parent::scopeScope($query, $publicId, $accountId);
+ return $query->with(['contact' => function($query) use ($clientId) {
+ return $query->whereClientId($clientId);
+ }]);
+ }
- if ($accountGatewayTokenId) {
- $query->where($this->getTable() . '.account_gateway_token_id', '=', $accountGatewayTokenId);
+ public function scopeIsBankAccount($query, $isBank)
+ {
+ if ($isBank) {
+ $query->where('payment_type_id', '=', PAYMENT_TYPE_ACH);
+ } else {
+ $query->where('payment_type_id', '!=', PAYMENT_TYPE_ACH);
}
+ }
- return $query;
+ public function imageUrl()
+ {
+ return url(sprintf('/images/credit_cards/%s.png', str_replace(' ', '', strtolower($this->payment_type->name))));
}
public static function lookupBankData($routingNumber) {
@@ -173,4 +163,4 @@ PaymentMethod::deleting(function($paymentMethod) {
$accountGatewayToken->default_payment_method_id = $newDefault ? $newDefault->id : null;
$accountGatewayToken->save();
}
-});
\ No newline at end of file
+});
diff --git a/app/Models/User.php b/app/Models/User.php
index 9dab5e759a15..96cb7bc1191e 100644
--- a/app/Models/User.php
+++ b/app/Models/User.php
@@ -280,6 +280,9 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac
} else {
$bitmask = 0;
foreach($value as $permission){
+ if ( ! $permission) {
+ continue;
+ }
$bitmask = $bitmask | static::$all_permissions[$permission];
}
diff --git a/app/Ninja/Datatables/AccountGatewayDatatable.php b/app/Ninja/Datatables/AccountGatewayDatatable.php
index fe6a8df0df7a..5d873b91de6b 100644
--- a/app/Ninja/Datatables/AccountGatewayDatatable.php
+++ b/app/Ninja/Datatables/AccountGatewayDatatable.php
@@ -49,12 +49,6 @@ class AccountGatewayDatatable extends EntityDatatable
}
}
],
- [
- 'payment_type',
- function ($model) {
- return Gateway::getPrettyPaymentType($model->gateway_id);
- }
- ],
];
}
diff --git a/app/Ninja/Mailers/ContactMailer.php b/app/Ninja/Mailers/ContactMailer.php
index 9fa2009a7b72..6ed657d15747 100644
--- a/app/Ninja/Mailers/ContactMailer.php
+++ b/app/Ninja/Mailers/ContactMailer.php
@@ -107,20 +107,6 @@ class ContactMailer extends Mailer
return $response;
}
- private function createAutoBillNotifyString($paymentMethod) {
- if ($paymentMethod->payment_type_id == PAYMENT_TYPE_DIRECT_DEBIT) {
- $paymentMethodString = trans('texts.auto_bill_payment_method_bank', ['bank'=>$paymentMethod->getBankName(), 'last4'=>$paymentMethod->last4]);
- } elseif ($paymentMethod->payment_type_id == PAYMENT_TYPE_ID_PAYPAL) {
- $paymentMethodString = trans('texts.auto_bill_payment_method_paypal', ['email'=>$paymentMethod->email]);
- } else {
- $code = str_replace(' ', '', strtolower($paymentMethod->payment_type->name));
- $cardType = trans("texts.card_" . $code);
- $paymentMethodString = trans('texts.auto_bill_payment_method_credit_card', ['type'=>$cardType,'last4'=>$paymentMethod->last4]);
- }
-
- return trans('texts.auto_bill_notification', ['payment_method'=>$paymentMethodString]);
- }
-
private function sendInvitation($invitation, $invoice, $body, $subject, $pdfString, $documentStrings)
{
$client = $invoice->client;
@@ -152,12 +138,14 @@ class ContactMailer extends Mailer
'amount' => $invoice->getRequestedAmount()
];
- if ($invoice->autoBillPaymentMethod) {
+ /*
+ if ($client->autoBillLater()) {
// Let the client know they'll be billed later
$variables['autobill'] = $this->createAutoBillNotifyString($invoice->autoBillPaymentMethod);
}
+ */
- if (empty($invitation->contact->password) && $account->hasFeature(FEATURE_CLIENT_PORTAL_PASSWORD) && $account->enable_portal_password && $account->send_portal_password) {
+ if (empty($invitation->contact->password) && $account->hasFeature(FEATURE_CLIENT_PORTAL_PASSWORD) && $account->enable_portal_password && $account->send_portal_password) {
// The contact needs a password
$variables['password'] = $password = $this->generatePassword();
$invitation->contact->password = bcrypt($password);
@@ -293,4 +281,20 @@ class ContactMailer extends Mailer
$this->sendTo($email, CONTACT_EMAIL, CONTACT_NAME, $subject, $view, $data);
}
+
+ /*
+ private function createAutoBillNotifyString($paymentMethod) {
+ if ($paymentMethod->payment_type_id == PAYMENT_TYPE_DIRECT_DEBIT) {
+ $paymentMethodString = trans('texts.auto_bill_payment_method_bank', ['bank'=>$paymentMethod->getBankName(), 'last4'=>$paymentMethod->last4]);
+ } elseif ($paymentMethod->payment_type_id == PAYMENT_TYPE_PAYPAL) {
+ $paymentMethodString = trans('texts.auto_bill_payment_method_paypal', ['email'=>$paymentMethod->email]);
+ } else {
+ $code = str_replace(' ', '', strtolower($paymentMethod->payment_type->name));
+ $cardType = trans("texts.card_" . $code);
+ $paymentMethodString = trans('texts.auto_bill_payment_method_credit_card', ['type'=>$cardType,'last4'=>$paymentMethod->last4]);
+ }
+
+ return trans('texts.auto_bill_notification', ['payment_method'=>$paymentMethodString]);
+ }
+ */
}
diff --git a/app/Ninja/PaymentDrivers/AuthorizeNetAIMPaymentDriver.php b/app/Ninja/PaymentDrivers/AuthorizeNetAIMPaymentDriver.php
new file mode 100644
index 000000000000..00ba9421ce03
--- /dev/null
+++ b/app/Ninja/PaymentDrivers/AuthorizeNetAIMPaymentDriver.php
@@ -0,0 +1,6 @@
+accountGateway = $accountGateway;
+ $this->invitation = $invitation;
+ $this->gatewayType = $gatewayType ?: $this->gatewayTypes()[0];
+ }
+
+ public function isGateway($gatewayId)
+ {
+ return $this->accountGateway->gateway_id == $gatewayId;
+ }
+
+ protected function isGatewayType($gatewayType)
+ {
+ return $this->gatewayType === $gatewayType;
+ }
+
+ protected function gatewayTypes()
+ {
+ return [
+ GATEWAY_TYPE_CREDIT_CARD
+ ];
+ }
+
+ public function handles($type)
+ {
+ return in_array($type, $this->gatewayTypes());
+ }
+
+ // when set to true we won't pass the card details with the form
+ public function tokenize()
+ {
+ return false;
+ }
+
+ // set payment method as pending until confirmed
+ public function isTwoStep()
+ {
+ return false;
+ }
+
+ public function providerName()
+ {
+ return strtolower($this->accountGateway->gateway->provider);
+ }
+
+ protected function invoice()
+ {
+ return $this->invitation->invoice;
+ }
+
+ protected function contact()
+ {
+ return $this->invitation->contact;
+ }
+
+ protected function client()
+ {
+ return $this->invoice()->client;
+ }
+
+ protected function account()
+ {
+ return $this->client()->account;
+ }
+
+ public function startPurchase($input = false, $sourceId = false)
+ {
+ $this->input = $input;
+ $this->sourceId = $sourceId;
+
+ Session::put('invitation_key', $this->invitation->invitation_key);
+ Session::put($this->invitation->id . 'gateway_type', $this->gatewayType);
+ Session::put($this->invitation->id . 'payment_ref', $this->invoice()->id . '_' . uniqid());
+
+ $gateway = $this->accountGateway->gateway;
+
+ if ($this->isGatewayType(GATEWAY_TYPE_TOKEN) || $gateway->is_offsite) {
+ if (Session::has('error')) {
+ Session::reflash();
+ } else {
+ $this->completeOnsitePurchase();
+ Session::flash('message', trans('texts.applied_payment'));
+ }
+
+ return redirect()->to('view/' . $this->invitation->invitation_key);
+ }
+
+ $data = [
+ 'accountGateway' => $this->accountGateway,
+ 'acceptedCreditCardTypes' => $this->accountGateway->getCreditcardTypes(),
+ 'gateway' => $gateway,
+ 'showAddress' => $this->accountGateway->show_address,
+ 'showBreadcrumbs' => false,
+ 'url' => 'payment/' . $this->invitation->invitation_key,
+ 'amount' => $this->invoice()->getRequestedAmount(),
+ 'invoiceNumber' => $this->invoice()->invoice_number,
+ 'client' => $this->client(),
+ 'contact' => $this->invitation->contact,
+ 'gatewayType' => $this->gatewayType,
+ 'currencyId' => $this->client()->getCurrencyId(),
+ 'currencyCode' => $this->client()->getCurrencyCode(),
+ 'account' => $this->account(),
+ 'sourceId' => $sourceId,
+ 'clientFontUrl' => $this->account()->getFontsUrl(),
+ 'tokenize' => $this->tokenize(),
+ 'transactionToken' => $this->createTransactionToken(),
+ ];
+
+ return view($this->paymentView(), $data);
+ }
+
+ // check if a custom view exists for this provider
+ protected function paymentView()
+ {
+ $file = sprintf('%s/views/payments/%s/%s.blade.php', resource_path(), $this->providerName(), $this->gatewayType);
+
+ if (file_exists($file)) {
+ return sprintf('payments.%s/%s', $this->providerName(), $this->gatewayType);
+ } else {
+ return sprintf('payments.%s', $this->gatewayType);
+ }
+ }
+
+ // check if a custom partial exists for this provider
+ public function partialView()
+ {
+ $file = sprintf('%s/views/payments/%s/partial.blade.php', resource_path(), $this->providerName());
+
+ if (file_exists($file)) {
+ return sprintf('payments.%s.partial', $this->providerName());
+ } else {
+ return false;
+ }
+ }
+
+ public function rules()
+ {
+ $rules = [];
+
+ if ($this->isGatewayType(GATEWAY_TYPE_CREDIT_CARD)) {
+
+ $rules = array_merge($rules, [
+ 'first_name' => 'required',
+ 'last_name' => 'required',
+ ]);
+
+ // TODO check this is always true
+ if ( ! $this->tokenize()) {
+ $rules = array_merge($rules, [
+ 'card_number' => 'required',
+ 'expiration_month' => 'required',
+ 'expiration_year' => 'required',
+ 'cvv' => 'required',
+ ]);
+ }
+
+ if ($this->accountGateway->show_address) {
+ $rules = array_merge($rules, [
+ 'address1' => 'required',
+ 'city' => 'required',
+ 'state' => 'required',
+ 'postal_code' => 'required',
+ 'country_id' => 'required',
+ ]);
+ }
+ }
+
+ return $rules;
+ }
+
+ protected function gateway()
+ {
+ if ($this->gateway) {
+ return $this->gateway;
+ }
+
+ $this->gateway = Omnipay::create($this->accountGateway->gateway->provider);
+ $this->gateway->initialize((array) $this->accountGateway->getConfig());
+
+ return $this->gateway;
+ }
+
+ public function completeOnsitePurchase($input = false, $paymentMethod = false)
+ {
+ $this->input = count($input) ? $input : false;
+ $gateway = $this->gateway();
+
+ if ($input) {
+ $this->updateAddress();
+ }
+
+ // load or create token
+ if ($this->isGatewayType(GATEWAY_TYPE_TOKEN)) {
+ if ( ! $paymentMethod) {
+ $paymentMethod = PaymentMethod::clientId($this->client()->id)
+ ->wherePublicId($this->sourceId)
+ ->firstOrFail();
+ }
+ } elseif ($this->shouldCreateToken()) {
+ $paymentMethod = $this->createToken();
+ }
+
+ if ($this->isTwoStep()) {
+ return;
+ }
+
+ // prepare and process payment
+ $data = $this->paymentDetails($paymentMethod);
+ $response = $gateway->purchase($data)->send();
+ $this->purchaseResponse = (array) $response->getData();
+
+ // parse the transaction reference
+ if ($this->transactionReferenceParam) {
+ $ref = $this->purchaseResponse[$this->transactionReferenceParam];
+ } else {
+ $ref = $response->getTransactionReference();
+ }
+
+ // wrap up
+ if ($response->isSuccessful() && $ref) {
+ $payment = $this->createPayment($ref, $paymentMethod);
+
+ // TODO move this to stripe driver
+ if ($this->invitation->invoice->account->account_key == NINJA_ACCOUNT_KEY) {
+ Session::flash('trackEventCategory', '/account');
+ Session::flash('trackEventAction', '/buy_pro_plan');
+ Session::flash('trackEventAmount', $payment->amount);
+ }
+
+ return $payment;
+ } elseif ($response->isRedirect()) {
+ $this->invitation->transaction_reference = $ref;
+ $this->invitation->save();
+ //Session::put('transaction_reference', $ref);
+ Session::save();
+ $response->redirect();
+ } else {
+ throw new Exception($response->getMessage() ?: trans('texts.payment_error'));
+ }
+ }
+
+ private function updateAddress()
+ {
+ if ( ! $this->isGatewayType(GATEWAY_TYPE_CREDIT_CARD)) {
+ return;
+ }
+
+ if ( ! $this->accountGateway->show_address || ! $this->accountGateway->update_address) {
+ return;
+ }
+
+ $client = $this->client();
+ $client->address1 = trim($this->input['address1']);
+ $client->address2 = trim($this->input['address2']);
+ $client->city = trim($this->input['city']);
+ $client->state = trim($this->input['state']);
+ $client->postal_code = trim($this->input['postal_code']);
+ $client->country_id = trim($this->input['country_id']);
+ $client->save();
+ }
+
+ protected function paymentDetails($paymentMethod = false)
+ {
+ $invoice = $this->invoice();
+ $completeUrl = url('complete/' . $this->invitation->invitation_key . '/' . $this->gatewayType);
+
+ $data = [
+ 'amount' => $invoice->getRequestedAmount(),
+ 'currency' => $invoice->getCurrencyCode(),
+ 'returnUrl' => $completeUrl,
+ 'cancelUrl' => $this->invitation->getLink(),
+ 'description' => trans('texts.' . $invoice->getEntityType()) . " {$invoice->invoice_number}",
+ 'transactionId' => $invoice->invoice_number,
+ 'transactionType' => 'Purchase',
+ 'ip' => Request::ip()
+ ];
+
+ if ($paymentMethod) {
+ if ($this->customerReferenceParam) {
+ $data[$this->customerReferenceParam] = $paymentMethod->account_gateway_token->token;
+ }
+ $data[$this->sourceReferenceParam] = $paymentMethod->source_reference;
+ } elseif ($this->input) {
+ $data['card'] = new CreditCard($this->paymentDetailsFromInput($this->input));
+ } else {
+ $data['card'] = new CreditCard($this->paymentDetailsFromClient());
+ }
+
+ return $data;
+ }
+
+ private function paymentDetailsFromInput($input)
+ {
+ $invoice = $this->invoice();
+ $client = $this->client();
+
+ $data = [
+ 'company' => $client->getDisplayName(),
+ 'firstName' => isset($input['first_name']) ? $input['first_name'] : null,
+ 'lastName' => isset($input['last_name']) ? $input['last_name'] : null,
+ 'email' => isset($input['email']) ? $input['email'] : null,
+ 'number' => isset($input['card_number']) ? $input['card_number'] : null,
+ 'expiryMonth' => isset($input['expiration_month']) ? $input['expiration_month'] : null,
+ 'expiryYear' => isset($input['expiration_year']) ? $input['expiration_year'] : null,
+ ];
+
+ // allow space until there's a setting to disable
+ if (isset($input['cvv']) && $input['cvv'] != ' ') {
+ $data['cvv'] = $input['cvv'];
+ }
+
+ if (isset($input['address1'])) {
+ // TODO use cache instead
+ $country = Country::find($input['country_id']);
+
+ $data = array_merge($data, [
+ 'billingAddress1' => $input['address1'],
+ 'billingAddress2' => $input['address2'],
+ 'billingCity' => $input['city'],
+ 'billingState' => $input['state'],
+ 'billingPostcode' => $input['postal_code'],
+ 'billingCountry' => $country->iso_3166_2,
+ 'shippingAddress1' => $input['address1'],
+ 'shippingAddress2' => $input['address2'],
+ 'shippingCity' => $input['city'],
+ 'shippingState' => $input['state'],
+ 'shippingPostcode' => $input['postal_code'],
+ 'shippingCountry' => $country->iso_3166_2
+ ]);
+ }
+
+ return $data;
+ }
+
+ public function paymentDetailsFromClient()
+ {
+ $invoice = $this->invoice();
+ $client = $this->client();
+ $contact = $this->invitation->contact ?: $client->contacts()->first();
+
+ return [
+ 'email' => $contact->email,
+ 'company' => $client->getDisplayName(),
+ 'firstName' => $contact->first_name,
+ 'lastName' => $contact->last_name,
+ 'billingAddress1' => $client->address1,
+ 'billingAddress2' => $client->address2,
+ 'billingCity' => $client->city,
+ 'billingPostcode' => $client->postal_code,
+ 'billingState' => $client->state,
+ 'billingCountry' => $client->country ? $client->country->iso_3166_2 : '',
+ 'billingPhone' => $contact->phone,
+ 'shippingAddress1' => $client->address1,
+ 'shippingAddress2' => $client->address2,
+ 'shippingCity' => $client->city,
+ 'shippingPostcode' => $client->postal_code,
+ 'shippingState' => $client->state,
+ 'shippingCountry' => $client->country ? $client->country->iso_3166_2 : '',
+ 'shippingPhone' => $contact->phone,
+ ];
+ }
+
+ protected function shouldCreateToken()
+ {
+ if ($this->isGatewayType(GATEWAY_TYPE_BANK_TRANSFER)) {
+ return true;
+ }
+
+ if ( ! $this->handles(GATEWAY_TYPE_TOKEN)) {
+ return false;
+ }
+
+ if ($this->account()->token_billing_type_id == TOKEN_BILLING_ALWAYS) {
+ return true;
+ }
+
+ return boolval(array_get($this->input, 'token_billing'));
+ }
+
+ /*
+ protected function tokenDetails()
+ {
+ $details = [];
+
+ if ($customer = $this->customer()) {
+ $details['customerReference'] = $customer->token;
+ }
+
+ return $details;
+ }
+ */
+
+ public function customer($clientId = false)
+ {
+ if ($this->customer) {
+ return $this->customer;
+ }
+
+ if ( ! $clientId) {
+ $clientId = $this->client()->id;
+ }
+
+ $this->customer = AccountGatewayToken::clientAndGateway($clientId, $this->accountGateway->id)
+ ->with('payment_methods')
+ ->first();
+
+ if ($this->customer) {
+ $this->customer = $this->checkCustomerExists($this->customer) ? $this->customer : null;
+ }
+
+ return $this->customer;
+ }
+
+ protected function checkCustomerExists($customer)
+ {
+ return true;
+ }
+
+ public function verifyBankAccount($client, $publicId, $amount1, $amount2)
+ {
+ throw new Exception('verifyBankAccount not implemented');
+ }
+
+ public function removePaymentMethod($paymentMethod)
+ {
+ $paymentMethod->delete();
+ }
+
+ // Some gateways (ie, Checkout.com and Braintree) require generating a token before paying for the invoice
+ public function createTransactionToken()
+ {
+ return null;
+ }
+
+ public function createToken()
+ {
+ $account = $this->account();
+
+ if ( ! $customer = $this->customer()) {
+ $customer = new AccountGatewayToken();
+ $customer->account_id = $account->id;
+ $customer->contact_id = $this->invitation->contact_id;
+ $customer->account_gateway_id = $this->accountGateway->id;
+ $customer->client_id = $this->client()->id;
+ $customer = $this->creatingCustomer($customer);
+ $customer->save();
+ }
+
+ /*
+ // archive the old payment method
+ $paymentMethod = PaymentMethod::clientId($this->client()->id)
+ ->isBankAccount($this->isGatewayType(GATEWAY_TYPE_BANK_TRANSFER))
+ ->first();
+
+ if ($paymentMethod) {
+ $paymentMethod->delete();
+ }
+ */
+
+ $paymentMethod = $this->createPaymentMethod($customer);
+
+ if ($paymentMethod && ! $customer->default_payment_method_id) {
+ $customer->default_payment_method_id = $paymentMethod->id;
+ $customer->save();
+ }
+
+ return $paymentMethod;
+ }
+
+ protected function creatingCustomer($customer)
+ {
+ return $customer;
+ }
+
+ public function createPaymentMethod($customer)
+ {
+ $paymentMethod = PaymentMethod::createNew($this->invitation);
+ $paymentMethod->ip = Request::ip();
+ $paymentMethod->account_gateway_token_id = $customer->id;
+ $paymentMethod->setRelation('account_gateway_token', $customer);
+ $paymentMethod = $this->creatingPaymentMethod($paymentMethod);
+
+ if ($paymentMethod) {
+ $paymentMethod->save();
+ }
+
+ return $paymentMethod;
+ }
+
+ protected function creatingPaymentMethod($paymentMethod)
+ {
+ return $paymentMethod;
+ }
+
+ public function deleteToken()
+ {
+
+ }
+
+ public function createPayment($ref = false, $paymentMethod = null)
+ {
+ $invitation = $this->invitation;
+ $invoice = $this->invoice();
+
+ $payment = Payment::createNew($invitation);
+ $payment->invitation_id = $invitation->id;
+ $payment->account_gateway_id = $this->accountGateway->id;
+ $payment->invoice_id = $invoice->id;
+ $payment->amount = $invoice->getRequestedAmount();
+ $payment->client_id = $invoice->client_id;
+ $payment->contact_id = $invitation->contact_id;
+ $payment->transaction_reference = $ref;
+ $payment->payment_date = date_create()->format('Y-m-d');
+ $payment->ip = Request::ip();
+
+ $payment = $this->creatingPayment($payment);
+
+ if ($paymentMethod) {
+ $payment->last4 = $paymentMethod->last4;
+ $payment->expiration = $paymentMethod->expiration;
+ $payment->routing_number = $paymentMethod->routing_number;
+ $payment->payment_type_id = $paymentMethod->payment_type_id;
+ $payment->email = $paymentMethod->email;
+ $payment->bank_name = $paymentMethod->bank_name;
+ $payment->payment_method_id = $paymentMethod->id;
+ }
+
+ $payment->save();
+
+ // TODO move this code
+ // enable pro plan for hosted users
+ if ($invoice->account->account_key == NINJA_ACCOUNT_KEY) {
+ foreach ($invoice->invoice_items as $invoice_item) {
+ // Hacky, but invoices don't have meta fields to allow us to store this easily
+ if (1 == preg_match('/^Plan - (.+) \((.+)\)$/', $invoice_item->product_key, $matches)) {
+ $plan = strtolower($matches[1]);
+ $term = strtolower($matches[2]);
+ } elseif ($invoice_item->product_key == 'Pending Monthly') {
+ $pending_monthly = true;
+ }
+ }
+
+ if (!empty($plan)) {
+ $account = Account::with('users')->find($invoice->client->public_id);
+
+ if(
+ $account->company->plan != $plan
+ || DateTime::createFromFormat('Y-m-d', $account->company->plan_expires) >= date_create('-7 days')
+ ) {
+ // Either this is a different plan, or the subscription expired more than a week ago
+ // Reset any grandfathering
+ $account->company->plan_started = date_create()->format('Y-m-d');
+ }
+
+ if (
+ $account->company->plan == $plan
+ && $account->company->plan_term == $term
+ && DateTime::createFromFormat('Y-m-d', $account->company->plan_expires) >= date_create()
+ ) {
+ // This is a renewal; mark it paid as of when this term expires
+ $account->company->plan_paid = $account->company->plan_expires;
+ } else {
+ $account->company->plan_paid = date_create()->format('Y-m-d');
+ }
+
+ $account->company->payment_id = $payment->id;
+ $account->company->plan = $plan;
+ $account->company->plan_term = $term;
+ $account->company->plan_expires = DateTime::createFromFormat('Y-m-d', $account->company->plan_paid)
+ ->modify($term == PLAN_TERM_MONTHLY ? '+1 month' : '+1 year')->format('Y-m-d');
+
+ if (!empty($pending_monthly)) {
+ $account->company->pending_plan = $plan;
+ $account->company->pending_term = PLAN_TERM_MONTHLY;
+ } else {
+ $account->company->pending_plan = null;
+ $account->company->pending_term = null;
+ }
+
+ $account->company->save();
+ }
+ }
+
+ return $payment;
+ }
+
+ protected function creatingPayment($payment)
+ {
+ return $payment;
+ }
+
+ public function refundPayment($payment, $amount)
+ {
+ $amount = min($amount, $payment->getCompletedAmount());
+
+ if ( ! $amount) {
+ return false;
+ }
+
+ if ($payment->payment_type_id == PAYMENT_TYPE_CREDIT) {
+ return $payment->recordRefund($amount);
+ }
+
+ $details = $this->refundDetails($payment, $amount);
+ $response = $this->gateway()->refund($details)->send();
+
+ if ($response->isSuccessful()) {
+ return $payment->recordRefund($amount);
+ } elseif ($this->attemptVoidPayment($response, $payment, $amount)) {
+ $details = ['transactionReference' => $payment->transaction_reference];
+ $response = $this->gateway->void($details)->send();
+ if ($response->isSuccessful()) {
+ return $payment->markVoided();
+ }
+ }
+
+ return false;
+ }
+
+ protected function refundDetails($payment, $amount)
+ {
+ return [
+ 'amount' => $amount,
+ 'transactionReference' => $payment->transaction_reference,
+ ];
+ }
+
+ protected function attemptVoidPayment($response, $payment, $amount)
+ {
+ // Partial refund not allowed for unsettled transactions
+ return $amount == $payment->amount;
+ }
+
+ protected function createLocalPayment($payment)
+ {
+ return $payment;
+ }
+
+ public function completeOffsitePurchase($input)
+ {
+ $this->input = $input;
+ $ref = array_get($this->input, 'token') ?: $this->invitation->transaction_reference;
+
+ if (method_exists($this->gateway(), 'completePurchase')) {
+
+ $details = $this->paymentDetails();
+ $response = $this->gateway()->completePurchase($details)->send();
+ $ref = $response->getTransactionReference() ?: $ref;
+
+ if ($response->isCancelled()) {
+ return false;
+ } elseif ( ! $response->isSuccessful()) {
+ throw new Exception($response->getMessage());
+ }
+ }
+
+ return $this->createPayment($ref);
+ }
+
+ public function tokenLinks()
+ {
+ if ( ! $this->customer()) {
+ return [];
+ }
+
+ $paymentMethods = $this->customer()->payment_methods;
+ $links = [];
+
+ foreach ($paymentMethods as $paymentMethod) {
+ if ($paymentMethod->payment_type_id == PAYMENT_TYPE_ACH && $paymentMethod->status != PAYMENT_METHOD_STATUS_VERIFIED) {
+ continue;
+ }
+
+ $url = URL::to("/payment/{$this->invitation->invitation_key}/token/".$paymentMethod->public_id);
+
+ if ($paymentMethod->payment_type_id == PAYMENT_TYPE_ACH) {
+ if ($paymentMethod->bank_name) {
+ $label = $paymentMethod->bank_name;
+ } else {
+ $label = trans('texts.use_bank_on_file');
+ }
+ } elseif ($paymentMethod->payment_type_id == PAYMENT_TYPE_PAYPAL) {
+ $label = 'PayPal: ' . $paymentMethod->email;
+ } else {
+ $label = trans('texts.use_card_on_file');
+ }
+
+ $links[] = [
+ 'url' => $url,
+ 'label' => $label,
+ ];
+ }
+
+ return $links;
+ }
+
+ public function paymentLinks()
+ {
+ $links = [];
+
+ foreach ($this->gatewayTypes() as $gatewayType) {
+ if ($gatewayType === GATEWAY_TYPE_TOKEN) {
+ continue;
+ }
+
+ $links[] = [
+ 'url' => $this->paymentUrl($gatewayType),
+ 'label' => trans("texts.{$gatewayType}")
+ ];
+ }
+
+ return $links;
+ }
+
+ protected function paymentUrl($gatewayType)
+ {
+ $account = $this->account();
+ $url = URL::to("/payment/{$this->invitation->invitation_key}/{$gatewayType}");
+
+ // PayPal doesn't allow being run in an iframe so we need to open in new tab
+ if ($gatewayType === GATEWAY_TYPE_PAYPAL) {
+ $url .= "#braintree_paypal";
+
+ if ($account->iframe_url) {
+ return 'javascript:window.open("' . $url . '", "_blank")';
+ }
+ }
+
+ return $url;
+ }
+
+ protected function parseCardType($cardName) {
+ $cardTypes = array(
+ 'visa' => PAYMENT_TYPE_VISA,
+ 'americanexpress' => PAYMENT_TYPE_AMERICAN_EXPRESS,
+ 'amex' => PAYMENT_TYPE_AMERICAN_EXPRESS,
+ 'mastercard' => PAYMENT_TYPE_MASTERCARD,
+ 'discover' => PAYMENT_TYPE_DISCOVER,
+ 'jcb' => PAYMENT_TYPE_JCB,
+ 'dinersclub' => PAYMENT_TYPE_DINERS,
+ 'carteblanche' => PAYMENT_TYPE_CARTE_BLANCHE,
+ 'chinaunionpay' => PAYMENT_TYPE_UNIONPAY,
+ 'unionpay' => PAYMENT_TYPE_UNIONPAY,
+ 'laser' => PAYMENT_TYPE_LASER,
+ 'maestro' => PAYMENT_TYPE_MAESTRO,
+ 'solo' => PAYMENT_TYPE_SOLO,
+ 'switch' => PAYMENT_TYPE_SWITCH
+ );
+
+ $cardName = strtolower(str_replace(array(' ', '-', '_'), '', $cardName));
+
+ if (empty($cardTypes[$cardName]) && 1 == preg_match('/^('.implode('|', array_keys($cardTypes)).')/', $cardName, $matches)) {
+ // Some gateways return extra stuff after the card name
+ $cardName = $matches[1];
+ }
+
+ if (!empty($cardTypes[$cardName])) {
+ return $cardTypes[$cardName];
+ } else {
+ return PAYMENT_TYPE_CREDIT_CARD_OTHER;
+ }
+ }
+}
diff --git a/app/Ninja/PaymentDrivers/BitPayPaymentDriver.php b/app/Ninja/PaymentDrivers/BitPayPaymentDriver.php
new file mode 100644
index 000000000000..420c876a7090
--- /dev/null
+++ b/app/Ninja/PaymentDrivers/BitPayPaymentDriver.php
@@ -0,0 +1,12 @@
+accountGateway->getPayPalEnabled()) {
+ $types[] = GATEWAY_TYPE_PAYPAL;
+ }
+
+ return $types;
+ }
+
+ public function tokenize()
+ {
+ return true;
+ }
+
+ public function startPurchase($input = false, $sourceId = false)
+ {
+ $data = parent::startPurchase($input, $sourceId);
+
+ if ($this->isGatewayType(GATEWAY_TYPE_PAYPAL)) {
+ /*
+ if ( ! $sourceId || empty($input['device_data'])) {
+ throw new Exception();
+ }
+
+ Session::put($this->invitation->id . 'device_data', $input['device_data']);
+ */
+
+ $data['details'] = ! empty($input['device_data']) ? json_decode($input['device_data']) : false;
+ }
+
+ return $data;
+ }
+
+ protected function checkCustomerExists($customer)
+ {
+ if ( ! parent::checkCustomerExists($customer)) {
+ return false;
+ }
+
+ $customer = $this->gateway()->findCustomer($customer->token)
+ ->send()
+ ->getData();
+
+ return ($customer instanceof Customer);
+ }
+
+ protected function paymentDetails($paymentMethod = false)
+ {
+ $data = parent::paymentDetails($paymentMethod);
+
+ $deviceData = array_get($this->input, 'device_data') ?: Session::get($this->invitation->id . 'device_data');
+
+ if ($deviceData) {
+ $data['device_data'] = $deviceData;
+ }
+
+ if ($this->isGatewayType(GATEWAY_TYPE_PAYPAL)) {
+ $data['ButtonSource'] = 'InvoiceNinja_SP';
+ }
+
+ if ( ! empty($this->input['sourceToken'])) {
+ $data['token'] = $this->input['sourceToken'];
+ }
+
+ return $data;
+ }
+
+ public function createToken()
+ {
+ if ($customer = $this->customer()) {
+ $customerReference = $customer->token;
+ } else {
+ $data = $this->paymentDetails();
+ $tokenResponse = $this->gateway()->createCustomer(['customerData' => $this->customerData()])->send();
+ if ($tokenResponse->isSuccessful()) {
+ $customerReference = $tokenResponse->getCustomerData()->id;
+ } else {
+ return false;
+ }
+ }
+
+ if ($customerReference) {
+ $data['customerId'] = $customerReference;
+
+ if ($this->isGatewayType(GATEWAY_TYPE_PAYPAL)) {
+ $data['paymentMethodNonce'] = $this->input['sourceToken'];
+ }
+
+ $tokenResponse = $this->gateway->createPaymentMethod($data)->send();
+ if ($tokenResponse->isSuccessful()) {
+ $this->tokenResponse = $tokenResponse->getData()->paymentMethod;
+ } else {
+ return false;
+ }
+ }
+
+ return parent::createToken();
+ }
+
+ private function customerData()
+ {
+ return [
+ 'firstName' => array_get($this->input, 'first_name') ?: $this->contact()->first_name,
+ 'lastName' => array_get($this->input, 'last_name') ?: $this->contact()->last_name,
+ 'company' => $this->client()->name,
+ 'email' => $this->contact()->email,
+ 'phone' => $this->contact()->phone,
+ 'website' => $this->client()->website
+ ];
+ }
+
+ public function creatingCustomer($customer)
+ {
+ $customer->token = $this->tokenResponse->customerId;
+
+ return $customer;
+ }
+
+ protected function creatingPaymentMethod($paymentMethod)
+ {
+ $response = $this->tokenResponse;
+
+ $paymentMethod->source_reference = $response->token;
+
+ if ($this->isGatewayType(GATEWAY_TYPE_CREDIT_CARD)) {
+ $paymentMethod->payment_type_id = $this->parseCardType($response->cardType);
+ $paymentMethod->last4 = $response->last4;
+ $paymentMethod->expiration = $response->expirationYear . '-' . $response->expirationMonth . '-01';
+ } elseif ($this->isGatewayType(GATEWAY_TYPE_PAYPAL)) {
+ $paymentMethod->email = $response->email;
+ $paymentMethod->payment_type_id = PAYMENT_TYPE_PAYPAL;
+ } else {
+ return null;
+ }
+
+ return $paymentMethod;
+ }
+
+ public function removePaymentMethod($paymentMethod)
+ {
+ $response = $this->gateway()->deletePaymentMethod([
+ 'token' => $paymentMethod->source_reference
+ ])->send();
+
+ if ($response->isSuccessful()) {
+ return parent::removePaymentMethod($paymentMethod);
+ } else {
+ throw new Exception($response->getMessage());
+ }
+ }
+
+ protected function attemptVoidPayment($response, $payment, $amount)
+ {
+ if ( ! parent::attemptVoidPayment($response, $payment, $amount)) {
+ return false;
+ }
+
+ $data = $response->getData();
+
+ if ($data instanceof \Braintree\Result\Error) {
+ $error = $data->errors->deepAll()[0];
+ if ($error && $error->code == 91506) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public function createTransactionToken()
+ {
+ return $this->gateway()
+ ->clientToken()
+ ->send()
+ ->getToken();
+ }
+}
diff --git a/app/Ninja/PaymentDrivers/CheckoutComPaymentDriver.php b/app/Ninja/PaymentDrivers/CheckoutComPaymentDriver.php
new file mode 100644
index 000000000000..ede0b678234b
--- /dev/null
+++ b/app/Ninja/PaymentDrivers/CheckoutComPaymentDriver.php
@@ -0,0 +1,35 @@
+gateway()->purchase([
+ 'amount' => $this->invoice()->getRequestedAmount(),
+ 'currency' => $this->client()->getCurrencyCode()
+ ])->send();
+
+ if ($response->isRedirect()) {
+ $token = $response->getTransactionReference();
+
+ $this->invitation->transaction_reference = $token;
+ $this->invitation->save();
+
+ return $token;
+ }
+
+ return false;
+ }
+
+ protected function paymentDetails($paymentMethod = false)
+ {
+ $data = parent::paymentDetails();
+
+ if ($ref = array_get($this->input, 'token')) {
+ $data['transactionReference'] = $ref;
+ }
+
+ return $data;
+ }
+
+}
diff --git a/app/Ninja/PaymentDrivers/CybersourcePaymentDriver.php b/app/Ninja/PaymentDrivers/CybersourcePaymentDriver.php
new file mode 100644
index 000000000000..29299eb94fb2
--- /dev/null
+++ b/app/Ninja/PaymentDrivers/CybersourcePaymentDriver.php
@@ -0,0 +1,15 @@
+createPayment($input['bill_trans_ref_no']);
+ } else {
+ throw new Exception($input['message'] . ': ' . $input['invalid_fields']);
+ }
+ }
+}
diff --git a/app/Ninja/PaymentDrivers/DwollaPaymentDriver.php b/app/Ninja/PaymentDrivers/DwollaPaymentDriver.php
new file mode 100644
index 000000000000..bfec26068a3f
--- /dev/null
+++ b/app/Ninja/PaymentDrivers/DwollaPaymentDriver.php
@@ -0,0 +1,24 @@
+getSandbox() && isset($_ENV['DWOLLA_SANDBOX_KEY']) && isset($_ENV['DWOLLA_SANSBOX_SECRET'])) {
+ $gateway->setKey($_ENV['DWOLLA_SANDBOX_KEY']);
+ $gateway->setSecret($_ENV['DWOLLA_SANSBOX_SECRET']);
+ } elseif (isset($_ENV['DWOLLA_KEY']) && isset($_ENV['DWOLLA_SECRET'])) {
+ $gateway->setKey($_ENV['DWOLLA_KEY']);
+ $gateway->setSecret($_ENV['DWOLLA_SECRET']);
+ }
+
+ return $gateway;
+ }
+}
diff --git a/app/Ninja/PaymentDrivers/EwayRapidSharedPaymentDriver.php b/app/Ninja/PaymentDrivers/EwayRapidSharedPaymentDriver.php
new file mode 100644
index 000000000000..fc842919e888
--- /dev/null
+++ b/app/Ninja/PaymentDrivers/EwayRapidSharedPaymentDriver.php
@@ -0,0 +1,6 @@
+paymentDetails();
+
+ $details['transactionReference'] = $this->invitation->transaction_reference;
+
+ $response = $this->gateway()->fetchTransaction($details)->send();
+
+ return $this->createPayment($response->getTransactionReference());
+ }
+
+}
diff --git a/app/Ninja/PaymentDrivers/PayFastPaymentDriver.php b/app/Ninja/PaymentDrivers/PayFastPaymentDriver.php
new file mode 100644
index 000000000000..79b49b23499e
--- /dev/null
+++ b/app/Ninja/PaymentDrivers/PayFastPaymentDriver.php
@@ -0,0 +1,13 @@
+isGateway(GATEWAY_PAYFAST) && Request::has('pt')) {
+ $token = Request::query('pt');
+ }
+ }
+}
diff --git a/app/Ninja/PaymentDrivers/PayPalExpressPaymentDriver.php b/app/Ninja/PaymentDrivers/PayPalExpressPaymentDriver.php
new file mode 100644
index 000000000000..88e3639efa4d
--- /dev/null
+++ b/app/Ninja/PaymentDrivers/PayPalExpressPaymentDriver.php
@@ -0,0 +1,29 @@
+payer_id = $this->input['PayerID'];
+
+ return $payment;
+ }
+}
diff --git a/app/Ninja/PaymentDrivers/PayPalProPaymentDriver.php b/app/Ninja/PaymentDrivers/PayPalProPaymentDriver.php
new file mode 100644
index 000000000000..9acf75e7cdfe
--- /dev/null
+++ b/app/Ninja/PaymentDrivers/PayPalProPaymentDriver.php
@@ -0,0 +1,20 @@
+accountGateway->getAchEnabled()) {
+ $types[] = GATEWAY_TYPE_BANK_TRANSFER;
+ }
+
+ return $types;
+ }
+
+ public function tokenize()
+ {
+ return $this->accountGateway->getPublishableStripeKey();
+ }
+
+ public function rules()
+ {
+ $rules = parent::rules();
+
+ if ($this->isGatewayType(GATEWAY_TYPE_BANK_TRANSFER)) {
+ $rules['authorize_ach'] = 'required';
+ }
+
+ return $rules;
+ }
+
+ protected function checkCustomerExists($customer)
+ {
+ $response = $this->gateway()
+ ->fetchCustomer(['customerReference' => $customer->token])
+ ->send();
+
+ if ( ! $response->isSuccessful()) {
+ return false;
+ }
+
+ $this->tokenResponse = $response->getData();
+
+ // import Stripe tokens created before payment methods table was added
+ if ( ! count($customer->payment_methods)) {
+ if ($paymentMethod = $this->createPaymentMethod($customer)) {
+ $customer->default_payment_method_id = $paymentMethod->id;
+ $customer->save();
+ $customer->load('payment_methods');
+ }
+ }
+
+ return true;
+ }
+
+ public function isTwoStep()
+ {
+ return $this->isGatewayType(GATEWAY_TYPE_BANK_TRANSFER) && empty($this->input['plaidPublicToken']);
+ }
+
+ protected function paymentDetails($paymentMethod = false)
+ {
+ $data = parent::paymentDetails($paymentMethod);
+
+ if ( ! empty($this->input['sourceToken'])) {
+ $data['token'] = $this->input['sourceToken'];
+ unset($data['card']);
+ }
+
+ if ( ! empty($this->input['plaidPublicToken'])) {
+ $data['plaidPublicToken'] = $this->input['plaidPublicToken'];
+ $data['plaidAccountId'] = $this->input['plaidAccountId'];
+ unset($data['card']);
+ }
+
+ return $data;
+ }
+
+ public function createToken()
+ {
+ $invoice = $this->invitation->invoice;
+ $client = $invoice->client;
+
+ $data = $this->paymentDetails();
+ $data['description'] = $client->getDisplayName();
+
+ if ( ! empty($data['plaidPublicToken'])) {
+ $plaidResult = $this->getPlaidToken($data['plaidPublicToken'], $data['plaidAccountId']);
+ unset($data['plaidPublicToken']);
+ unset($data['plaidAccountId']);
+ $data['token'] = $plaidResult['stripe_bank_account_token'];
+ }
+
+ // if a customer already exists link the token to it
+ if ($customer = $this->customer()) {
+ $data['customerReference'] = $customer->token;
+ }
+
+ $tokenResponse = $this->gateway()
+ ->createCard($data)
+ ->send();
+
+ if ($tokenResponse->isSuccessful()) {
+ $this->tokenResponse = $tokenResponse->getData();
+
+ return parent::createToken();
+ } else {
+ throw new Exception($tokenResponse->getMessage());
+ }
+ }
+
+ public function creatingCustomer($customer)
+ {
+ $customer->token = $this->tokenResponse['id'];
+
+ return $customer;
+ }
+
+ protected function creatingPaymentMethod($paymentMethod)
+ {
+ $data = $this->tokenResponse;
+
+ if (!empty($data['object']) && ($data['object'] == 'card' || $data['object'] == 'bank_account')) {
+ $source = $data;
+ } elseif (!empty($data['object']) && $data['object'] == 'customer') {
+ $sources = !empty($data['sources']) ? $data['sources'] : $data['cards'];
+ $source = reset($sources['data']);
+ } else {
+ $source = !empty($data['source']) ? $data['source'] : $data['card'];
+ }
+
+ if ( ! $source) {
+ return false;
+ }
+
+ $paymentMethod->source_reference = $source['id'];
+ $paymentMethod->last4 = $source['last4'];
+
+ if ($this->isGatewayType(GATEWAY_TYPE_CREDIT_CARD)) {
+
+ $paymentMethod->expiration = $source['exp_year'] . '-' . $source['exp_month'] . '-01';
+ $paymentMethod->payment_type_id = $this->parseCardType($source['brand']);
+
+ } elseif ($this->isGatewayType(GATEWAY_TYPE_BANK_TRANSFER)) {
+
+ $paymentMethod->routing_number = $source['routing_number'];
+ $paymentMethod->payment_type_id = PAYMENT_TYPE_ACH;
+ $paymentMethod->status = $source['status'];
+ $currency = Cache::get('currencies')->where('code', strtoupper($source['currency']))->first();
+
+ if ($currency) {
+ $paymentMethod->currency_id = $currency->id;
+ $paymentMethod->setRelation('currency', $currency);
+ }
+
+ }
+
+ return $paymentMethod;
+ }
+
+ protected function creatingPayment($payment)
+ {
+ if ($this->isGatewayType(GATEWAY_TYPE_BANK_TRANSFER)) {
+ $payment->payment_status_id = $this->purchaseResponse['status'] == 'succeeded' ? PAYMENT_STATUS_COMPLETED : PAYMENT_STATUS_PENDING;
+ }
+
+ return $payment;
+ }
+
+ public function removePaymentMethod($paymentMethod)
+ {
+ if ( ! $paymentMethod->relationLoaded('account_gateway_token')) {
+ $paymentMethod->load('account_gateway_token');
+ }
+
+ $response = $this->gateway()->deleteCard([
+ 'customerReference' => $paymentMethod->account_gateway_token->token,
+ 'cardReference' => $paymentMethod->source_reference
+ ])->send();
+
+ if ($response->isSuccessful()) {
+ return parent::removePaymentMethod($paymentMethod);
+ } else {
+ throw new Exception($response->getMessage());
+ }
+ }
+
+ private function getPlaidToken($publicToken, $accountId)
+ {
+ $clientId = $this->accountGateway->getPlaidClientId();
+ $secret = $this->accountGateway->getPlaidSecret();
+
+ if (!$clientId) {
+ throw new Exception('plaid client id not set'); // TODO use text strings
+ }
+
+ if (!$secret) {
+ throw new Exception('plaid secret not set');
+ }
+
+ try {
+ $subdomain = $this->accountGateway->getPlaidEnvironment() == 'production' ? 'api' : 'tartan';
+ $response = (new \GuzzleHttp\Client(['base_uri'=>"https://{$subdomain}.plaid.com"]))->request(
+ 'POST',
+ 'exchange_token',
+ [
+ 'allow_redirects' => false,
+ 'headers' => ['content-type' => 'application/x-www-form-urlencoded'],
+ 'body' => http_build_query(array(
+ 'client_id' => $clientId,
+ 'secret' => $secret,
+ 'public_token' => $publicToken,
+ 'account_id' => $accountId,
+ ))
+ ]
+ );
+ return json_decode($response->getBody(), true);
+ } catch (\GuzzleHttp\Exception\BadResponseException $e) {
+ $response = $e->getResponse();
+ $body = json_decode($response->getBody(), true);
+
+ if ($body && !empty($body['message'])) {
+ throw new Exception($body['message']);
+ } else {
+ throw new Exception($e->getMessage());
+ }
+ }
+ }
+
+ public function verifyBankAccount($client, $publicId, $amount1, $amount2)
+ {
+ $customer = $this->customer($client->id);
+ $paymentMethod = PaymentMethod::clientId($client->id)
+ ->wherePublicId($publicId)
+ ->firstOrFail();
+
+ // Omnipay doesn't support verifying payment methods
+ // Also, it doesn't want to urlencode without putting numbers inside the brackets
+ $result = $this->makeStripeCall(
+ 'POST',
+ 'customers/' . $customer->token . '/sources/' . $paymentMethod->source_reference . '/verify',
+ 'amounts[]=' . intval($amount1) . '&amounts[]=' . intval($amount2)
+ );
+
+ if (is_string($result)) {
+ return $result;
+ }
+
+ $paymentMethod->status = PAYMENT_METHOD_STATUS_VERIFIED;
+ $paymentMethod->save();
+
+ if ( ! $customer->default_payment_method_id) {
+ $customer->default_payment_method_id = $paymentMethod->id;
+ $customer->save();
+ }
+
+ return true;
+ }
+
+ public function makeStripeCall($method, $url, $body = null)
+ {
+ $apiKey = $this->accountGateway->getConfig()->apiKey;
+
+ if (!$apiKey) {
+ return 'No API key set';
+ }
+
+ try{
+ $options = [
+ 'headers' => ['content-type' => 'application/x-www-form-urlencoded'],
+ 'auth' => [$apiKey, ''],
+ ];
+
+ if ($body) {
+ $options['body'] = $body;
+ }
+
+ $response = (new \GuzzleHttp\Client(['base_uri'=>'https://api.stripe.com/v1/']))->request(
+ $method,
+ $url,
+ $options
+ );
+ return json_decode($response->getBody(), true);
+ } catch (\GuzzleHttp\Exception\BadResponseException $e) {
+ $response = $e->getResponse();
+
+ $body = json_decode($response->getBody(), true);
+ if ($body && $body['error'] && $body['error']['type'] == 'invalid_request_error') {
+ return $body['error']['message'];
+ }
+
+ return $e->getMessage();
+ }
+ }
+}
diff --git a/app/Ninja/PaymentDrivers/TwoCheckoutPaymentDriver.php b/app/Ninja/PaymentDrivers/TwoCheckoutPaymentDriver.php
new file mode 100644
index 000000000000..005611d4a034
--- /dev/null
+++ b/app/Ninja/PaymentDrivers/TwoCheckoutPaymentDriver.php
@@ -0,0 +1,13 @@
+createPayment($input['order_number']);
+ }
+
+}
diff --git a/app/Ninja/PaymentDrivers/WePayPaymentDriver.php b/app/Ninja/PaymentDrivers/WePayPaymentDriver.php
new file mode 100644
index 000000000000..05561fb69d18
--- /dev/null
+++ b/app/Ninja/PaymentDrivers/WePayPaymentDriver.php
@@ -0,0 +1,118 @@
+isGatewayType(GATEWAY_TYPE_BANK_TRANSFER)) {
+ if ( ! $sourceId) {
+ throw new Exception();
+ }
+ }
+
+ return $data;
+ }
+
+ public function tokenize()
+ {
+ return true;
+ }
+
+ protected function checkCustomerExists($customer)
+ {
+ return true;
+ }
+
+ public function rules()
+ {
+ $rules = parent::rules();
+
+ if ($this->isGatewayType(GATEWAY_TYPE_BANK_TRANSFER)) {
+ $rules = array_merge($rules, [
+ 'authorize_ach' => 'required',
+ 'tos_agree' => 'required',
+ ]);
+ }
+
+ return $rules;
+ }
+
+ protected function paymentDetails($paymentMethod = false)
+ {
+ $data = parent::paymentDetails($paymentMethod);
+
+ if ($transactionId = Session::get($invitation->id . 'payment_ref')) {
+ $data['transaction_id'] = $transactionId;
+ }
+
+ $data['applicationFee'] = $this->calculateApplicationFee($data['amount']);
+ $data['feePayer'] = WEPAY_FEE_PAYER;
+ $data['callbackUri'] = $this->accountGateway->getWebhookUrl();
+
+ if ($this->isGatewayType(GATEWAY_TYPE_BANK_TRANSFER)) {
+ $data['paymentMethodType'] = 'payment_bank';
+ }
+
+ return $data;
+ }
+
+ public function removePaymentMethod($paymentMethod)
+ {
+ $wepay = Utils::setupWePay($this->accountGateway);
+ $wepay->request('/credit_card/delete', [
+ 'client_id' => WEPAY_CLIENT_ID,
+ 'client_secret' => WEPAY_CLIENT_SECRET,
+ 'credit_card_id' => intval($paymentMethod->source_reference),
+ ]);
+
+ if ($response->isSuccessful()) {
+ return parent::removePaymentMethod($paymentMethod);
+ } else {
+ throw new Exception($response->getMessage());
+ }
+ }
+
+ protected function refundDetails($payment, $amount)
+ {
+ $data = parent::refundDetails($parent);
+
+ $data['refund_reason'] = 'Refund issued by merchant.';
+
+ // WePay issues a full refund when no amount is set. If an amount is set, it will try
+ // to issue a partial refund without refunding any fees. However, the Stripe driver
+ // (but not the API) requires the amount parameter to be set no matter what.
+ if ($data['amount'] == $payment->getCompletedAmount()) {
+ unset($data['amount']);
+ }
+
+ return $data;
+ }
+
+ protected function attemptVoidPayment($response, $payment, $amount)
+ {
+ if ( ! parent::attemptVoidPayment($response, $payment, $amount)) {
+ return false;
+ }
+
+ return $response->getCode() == 4004;
+ }
+
+ private function calculateApplicationFee($amount)
+ {
+ $fee = WEPAY_APP_FEE_MULTIPLIER * $amount + WEPAY_APP_FEE_FIXED;
+
+ return floor(min($fee, $amount * 0.2));// Maximum fee is 20% of the amount.
+ }
+
+}
diff --git a/app/Ninja/Repositories/InvoiceRepository.php b/app/Ninja/Repositories/InvoiceRepository.php
index 91ac711390bc..d76c1902a75a 100644
--- a/app/Ninja/Repositories/InvoiceRepository.php
+++ b/app/Ninja/Repositories/InvoiceRepository.php
@@ -741,6 +741,7 @@ class InvoiceRepository extends BaseRepository
}
$invoice = Invoice::createNew($recurInvoice);
+ $invoice->invoice_type_id = INVOICE_TYPE_STANDARD;
$invoice->client_id = $recurInvoice->client_id;
$invoice->recurring_invoice_id = $recurInvoice->id;
$invoice->invoice_number = $recurInvoice->account->getNextInvoiceNumber($invoice);
diff --git a/app/Services/DatatableService.php b/app/Services/DatatableService.php
index 8fa49df5f7df..5126bd48d6a9 100644
--- a/app/Services/DatatableService.php
+++ b/app/Services/DatatableService.php
@@ -114,10 +114,8 @@ class DatatableService
. trans("texts.archive_{$datatable->entityType}") . "";
}
} else if($can_edit) {
- if ($datatable->entityType != ENTITY_ACCOUNT_GATEWAY || Auth::user()->account->canAddGateway(\App\Models\Gateway::getPaymentType($model->gateway_id))) {
- $dropdown_contents .= "public_id})\">"
- . trans("texts.restore_{$datatable->entityType}") . "";
- }
+ $dropdown_contents .= "public_id})\">"
+ . trans("texts.restore_{$datatable->entityType}") . "";
}
if (property_exists($model, 'is_deleted') && !$model->is_deleted && $can_edit) {
diff --git a/app/Services/InvoiceService.php b/app/Services/InvoiceService.php
index a6dc8d873d39..d5a2ab7f1218 100644
--- a/app/Services/InvoiceService.php
+++ b/app/Services/InvoiceService.php
@@ -35,14 +35,19 @@ class InvoiceService extends BaseService
{
if (isset($data['client'])) {
$canSaveClient = false;
+ $canViewClient = false;
$clientPublicId = array_get($data, 'client.public_id') ?: array_get($data, 'client.id');
if (empty($clientPublicId) || $clientPublicId == '-1') {
$canSaveClient = Auth::user()->can('create', ENTITY_CLIENT);
} else {
- $canSaveClient = Auth::user()->can('edit', Client::scope($clientPublicId)->first());
+ $client = Client::scope($clientPublicId)->first();
+ $canSaveClient = Auth::user()->can('edit', $client);
+ $canViewClient = Auth::user()->can('view', $client);
}
if ($canSaveClient) {
$client = $this->clientRepo->save($data['client']);
+ }
+ if ($canSaveClient || $canViewClient) {
$data['client_id'] = $client->id;
}
}
diff --git a/app/Services/PaymentService.php b/app/Services/PaymentService.php
index 3070671b3595..1f0b594fae3a 100644
--- a/app/Services/PaymentService.php
+++ b/app/Services/PaymentService.php
@@ -43,360 +43,9 @@ class PaymentService extends BaseService
return $this->paymentRepo;
}
- public function createGateway($accountGateway)
- {
- $gateway = Omnipay::create($accountGateway->gateway->provider);
- $gateway->initialize((array)$accountGateway->getConfig());
-
- if ($accountGateway->isGateway(GATEWAY_DWOLLA)) {
- if ($gateway->getSandbox() && isset($_ENV['DWOLLA_SANDBOX_KEY']) && isset($_ENV['DWOLLA_SANSBOX_SECRET'])) {
- $gateway->setKey($_ENV['DWOLLA_SANDBOX_KEY']);
- $gateway->setSecret($_ENV['DWOLLA_SANSBOX_SECRET']);
- } elseif (isset($_ENV['DWOLLA_KEY']) && isset($_ENV['DWOLLA_SECRET'])) {
- $gateway->setKey($_ENV['DWOLLA_KEY']);
- $gateway->setSecret($_ENV['DWOLLA_SECRET']);
- }
- }
-
- return $gateway;
- }
-
- public function getPaymentDetails($invitation, $accountGateway, $input = null)
- {
- $invoice = $invitation->invoice;
- $account = $invoice->account;
- $key = $invoice->account_id . '-' . $invoice->invoice_number;
- $currencyCode = $invoice->client->currency ? $invoice->client->currency->code : ($invoice->account->currency ? $invoice->account->currency->code : 'USD');
-
- if ($input) {
- $data = self::convertInputForOmnipay($input);
- Session::put($key, $data);
- } elseif (Session::get($key)) {
- $data = Session::get($key);
- } else {
- $data = $this->createDataForClient($invitation);
- }
-
- $card = !empty($data['number']) ? new CreditCard($data) : null;
- $data = [
- 'amount' => $invoice->getRequestedAmount(),
- 'card' => $card,
- 'currency' => $currencyCode,
- 'returnUrl' => URL::to('complete'),
- 'cancelUrl' => $invitation->getLink(),
- 'description' => trans('texts.' . $invoice->getEntityType()) . " {$invoice->invoice_number}",
- 'transactionId' => $invoice->invoice_number,
- 'transactionType' => 'Purchase',
- ];
-
- if ($input !== null) {
- $data['ip'] = \Request::ip();
- }
-
- if ($accountGateway->isGateway(GATEWAY_PAYPAL_EXPRESS) || $accountGateway->isGateway(GATEWAY_PAYPAL_PRO)) {
- $data['ButtonSource'] = 'InvoiceNinja_SP';
- };
-
- if ($input) {
- if (!empty($input['sourceToken'])) {
- $data['token'] = $input['sourceToken'];
- unset($data['card']);
- } elseif (!empty($input['plaidPublicToken'])) {
- $data['plaidPublicToken'] = $input['plaidPublicToken'];
- $data['plaidAccountId'] = $input['plaidAccountId'];
- unset($data['card']);
- }
- }
-
- if ($accountGateway->isGateway(GATEWAY_WEPAY) && $transactionId = Session::get($invitation->id.'payment_ref')) {
- $data['transaction_id'] = $transactionId;
- }
-
- return $data;
- }
-
- public function convertInputForOmnipay($input)
- {
- $data = [
- 'firstName' => isset($input['first_name']) ? $input['first_name'] : null,
- 'lastName' =>isset($input['last_name']) ? $input['last_name'] : null,
- 'email' => isset($input['email']) ? $input['email'] : null,
- 'number' => isset($input['card_number']) ? $input['card_number'] : null,
- 'expiryMonth' => isset($input['expiration_month']) ? $input['expiration_month'] : null,
- 'expiryYear' => isset($input['expiration_year']) ? $input['expiration_year'] : null,
- ];
-
- // allow space until there's a setting to disable
- if (isset($input['cvv']) && $input['cvv'] != ' ') {
- $data['cvv'] = $input['cvv'];
- }
-
- if (isset($input['address1'])) {
- $country = Country::find($input['country_id']);
-
- $data = array_merge($data, [
- 'billingAddress1' => $input['address1'],
- 'billingAddress2' => $input['address2'],
- 'billingCity' => $input['city'],
- 'billingState' => $input['state'],
- 'billingPostcode' => $input['postal_code'],
- 'billingCountry' => $country->iso_3166_2,
- 'shippingAddress1' => $input['address1'],
- 'shippingAddress2' => $input['address2'],
- 'shippingCity' => $input['city'],
- 'shippingState' => $input['state'],
- 'shippingPostcode' => $input['postal_code'],
- 'shippingCountry' => $country->iso_3166_2
- ]);
- }
-
- return $data;
- }
-
- public function createDataForClient($invitation)
- {
- $invoice = $invitation->invoice;
- $client = $invoice->client;
- $contact = $invitation->contact ?: $client->contacts()->first();
-
- return [
- 'email' => $contact->email,
- 'company' => $client->getDisplayName(),
- 'firstName' => $contact->first_name,
- 'lastName' => $contact->last_name,
- 'billingAddress1' => $client->address1,
- 'billingAddress2' => $client->address2,
- 'billingCity' => $client->city,
- 'billingPostcode' => $client->postal_code,
- 'billingState' => $client->state,
- 'billingCountry' => $client->country ? $client->country->iso_3166_2 : '',
- 'billingPhone' => $contact->phone,
- 'shippingAddress1' => $client->address1,
- 'shippingAddress2' => $client->address2,
- 'shippingCity' => $client->city,
- 'shippingPostcode' => $client->postal_code,
- 'shippingState' => $client->state,
- 'shippingCountry' => $client->country ? $client->country->iso_3166_2 : '',
- 'shippingPhone' => $contact->phone,
- ];
- }
-
- public function getClientPaymentMethods($client)
- {
- $token = $client->getGatewayToken($accountGateway/* return parameter */, $accountGatewayToken/* return parameter */);
- if (!$token) {
- return null;
- }
-
- if (!$accountGatewayToken->uses_local_payment_methods && $accountGateway->gateway_id == GATEWAY_STRIPE) {
- // Migrate Stripe data
- $gateway = $this->createGateway($accountGateway);
- $response = $gateway->fetchCustomer(array('customerReference' => $token))->send();
- if (!$response->isSuccessful()) {
- return null;
- }
-
- $data = $response->getData();
- $sources_list = isset($data['sources']) ? $data['sources'] : $data['cards'];
- $sources = isset($sources_list['data'])?$sources_list['data']:$sources_list;
-
- // Load
- $accountGatewayToken->payment_methods();
- foreach ($sources as $source) {
- $paymentMethod = $this->convertPaymentMethodFromStripe($source, $accountGatewayToken);
- if ($paymentMethod) {
- $paymentMethod->save();
- }
-
- if ($data['default_source'] == $source['id']) {
- $accountGatewayToken->default_payment_method_id = $paymentMethod->id;
- }
- }
-
- $accountGatewayToken->uses_local_payment_methods = true;
- $accountGatewayToken->save();
- }
-
- return $accountGatewayToken->payment_methods;
- }
-
- public function verifyClientPaymentMethod($client, $publicId, $amount1, $amount2)
- {
- $token = $client->getGatewayToken($accountGateway/* return parameter */, $accountGatewayToken/* return parameter */);
- if ($accountGateway->gateway_id != GATEWAY_STRIPE) {
- return 'Unsupported gateway';
- }
-
- $paymentMethod = PaymentMethod::scope($publicId, $client->account_id, $accountGatewayToken->id)->firstOrFail();
-
- // Omnipay doesn't support verifying payment methods
- // Also, it doesn't want to urlencode without putting numbers inside the brackets
- $result = $this->makeStripeCall(
- $accountGateway,
- 'POST',
- 'customers/' . $token . '/sources/' . $paymentMethod->source_reference . '/verify',
- 'amounts[]=' . intval($amount1) . '&amounts[]=' . intval($amount2)
- );
-
- if (is_string($result)) {
- return $result;
- }
-
- $paymentMethod->status = PAYMENT_METHOD_STATUS_VERIFIED;
- $paymentMethod->save();
-
- if (!$paymentMethod->account_gateway_token->default_payment_method_id) {
- $paymentMethod->account_gateway_token->default_payment_method_id = $paymentMethod->id;
- $paymentMethod->account_gateway_token->save();
- }
-
- return true;
- }
-
- public function removeClientPaymentMethod($client, $publicId)
- {
- $token = $client->getGatewayToken($accountGateway/* return parameter */, $accountGatewayToken/* return parameter */);
- if (!$token) {
- return null;
- }
-
- $paymentMethod = PaymentMethod::scope($publicId, $client->account_id, $accountGatewayToken->id)->firstOrFail();
-
- $gateway = $this->createGateway($accountGateway);
-
- if ($accountGateway->gateway_id == GATEWAY_STRIPE) {
- $response = $gateway->deleteCard(array('customerReference' => $token, 'cardReference' => $paymentMethod->source_reference))->send();
- if (!$response->isSuccessful()) {
- return $response->getMessage();
- }
- } elseif ($accountGateway->gateway_id == GATEWAY_BRAINTREE) {
- $response = $gateway->deletePaymentMethod(array('token' => $paymentMethod->source_reference))->send();
-
- if (!$response->isSuccessful()) {
- return $response->getMessage();
- }
- } elseif ($accountGateway->gateway_id == GATEWAY_WEPAY) {
- try {
- $wepay = Utils::setupWePay($accountGateway);
- $wepay->request('/credit_card/delete', [
- 'client_id' => WEPAY_CLIENT_ID,
- 'client_secret' => WEPAY_CLIENT_SECRET,
- 'credit_card_id' => intval($paymentMethod->source_reference),
- ]);
- } catch (\WePayException $ex){
- return $ex->getMessage();
- }
- }
-
- $paymentMethod->delete();
-
- return true;
- }
-
- public function setClientDefaultPaymentMethod($client, $publicId)
- {
- $token = $client->getGatewayToken($accountGateway/* return parameter */, $accountGatewayToken/* return parameter */);
- if (!$token) {
- return null;
- }
-
- $paymentMethod = PaymentMethod::scope($publicId, $client->account_id, $accountGatewayToken->id)->firstOrFail();
- $paymentMethod->account_gateway_token->default_payment_method_id = $paymentMethod->id;
- $paymentMethod->account_gateway_token->save();
-
- return true;
- }
-
public function createToken($paymentType, $gateway, $details, $accountGateway, $client, $contactId, &$customerReference = null, &$paymentMethod = null)
{
- $customerReference = $client->getGatewayToken($accountGateway, $accountGatewayToken/* return paramenter */);
-
- if ($customerReference && $customerReference != CUSTOMER_REFERENCE_LOCAL) {
- $details['customerReference'] = $customerReference;
-
- if ($accountGateway->gateway_id == GATEWAY_STRIPE) {
- $customerResponse = $gateway->fetchCustomer(array('customerReference' => $customerReference))->send();
-
- if (!$customerResponse->isSuccessful()) {
- $customerReference = null; // The customer might not exist anymore
- }
- } elseif ($accountGateway->gateway_id == GATEWAY_BRAINTREE) {
- $customer = $gateway->findCustomer($customerReference)->send()->getData();
-
- if (!($customer instanceof \Braintree\Customer)) {
- $customerReference = null; // The customer might not exist anymore
- }
- }
- }
-
- if ($accountGateway->gateway_id == GATEWAY_STRIPE) {
- if (!empty($details['plaidPublicToken'])) {
- $plaidResult = $this->getPlaidToken($accountGateway, $details['plaidPublicToken'], $details['plaidAccountId']);
-
- if (is_string($plaidResult)) {
- $this->lastError = $plaidResult;
- return;
- } elseif (!$plaidResult) {
- $this->lastError = 'No token received from Plaid';
- return;
- }
-
- unset($details['plaidPublicToken']);
- unset($details['plaidAccountId']);
- $details['token'] = $plaidResult['stripe_bank_account_token'];
- }
-
- $tokenResponse = $gateway->createCard($details)->send();
-
- if ($tokenResponse->isSuccessful()) {
- $sourceReference = $tokenResponse->getCardReference();
- if (!$customerReference) {
- $customerReference = $tokenResponse->getCustomerReference();
- }
-
- if (!$sourceReference) {
- $responseData = $tokenResponse->getData();
- if (!empty($responseData['object']) && ($responseData['object'] == 'bank_account' || $responseData['object'] == 'card')) {
- $sourceReference = $responseData['id'];
- }
- }
-
- if ($customerReference == $sourceReference) {
- // This customer was just created; find the card
- $data = $tokenResponse->getData();
- if (!empty($data['default_source'])) {
- $sourceReference = $data['default_source'];
- }
- }
- } else {
- $data = $tokenResponse->getData();
- if ($data && $data['error'] && $data['error']['type'] == 'invalid_request_error') {
- $this->lastError = $data['error']['message'];
- return;
- }
- }
- } elseif ($accountGateway->gateway_id == GATEWAY_BRAINTREE) {
- if (!$customerReference) {
- $tokenResponse = $gateway->createCustomer(array('customerData' => array()))->send();
- if ($tokenResponse->isSuccessful()) {
- $customerReference = $tokenResponse->getCustomerData()->id;
- } else {
- $this->lastError = $tokenResponse->getData()->message;
- return;
- }
- }
-
- if ($customerReference) {
- $details['customerId'] = $customerReference;
-
- $tokenResponse = $gateway->createPaymentMethod($details)->send();
- if ($tokenResponse->isSuccessful()) {
- $sourceReference = $tokenResponse->getData()->paymentMethod->token;
- } else {
- $this->lastError = $tokenResponse->getData()->message;
- return;
- }
- }
+ if ($accountGateway->gateway_id == GATEWAY_BRAINTREE) {
} elseif ($accountGateway->gateway_id == GATEWAY_WEPAY) {
$wepay = Utils::setupWePay($accountGateway);
try {
@@ -466,60 +115,6 @@ class PaymentService extends BaseService
return $sourceReference;
}
- public function convertPaymentMethodFromStripe($source, $accountGatewayToken = null, $paymentMethod = null) {
- // Creating a new one or updating an existing one
- if (!$paymentMethod) {
- $paymentMethod = $accountGatewayToken ? PaymentMethod::createNew($accountGatewayToken) : new PaymentMethod();
- }
-
- $paymentMethod->last4 = $source['last4'];
- $paymentMethod->source_reference = $source['id'];
-
- if ($source['object'] == 'bank_account') {
- $paymentMethod->routing_number = $source['routing_number'];
- $paymentMethod->payment_type_id = PAYMENT_TYPE_ACH;
- $paymentMethod->status = $source['status'];
- $currency = Cache::get('currencies')->where('code', strtoupper($source['currency']))->first();
- if ($currency) {
- $paymentMethod->currency_id = $currency->id;
- $paymentMethod->setRelation('currency', $currency);
- }
- } elseif ($source['object'] == 'card') {
- $paymentMethod->expiration = $source['exp_year'] . '-' . $source['exp_month'] . '-01';
- $paymentMethod->payment_type_id = $this->parseCardType($source['brand']);
- } else {
- return null;
- }
-
- $paymentMethod->setRelation('payment_type', Cache::get('paymentTypes')->find($paymentMethod->payment_type_id));
-
- return $paymentMethod;
- }
-
- public function convertPaymentMethodFromBraintree($source, $accountGatewayToken = null, $paymentMethod = null) {
- // Creating a new one or updating an existing one
- if (!$paymentMethod) {
- $paymentMethod = $accountGatewayToken ? PaymentMethod::createNew($accountGatewayToken) : new PaymentMethod();
- }
-
- if ($source instanceof \Braintree\CreditCard) {
- $paymentMethod->payment_type_id = $this->parseCardType($source->cardType);
- $paymentMethod->last4 = $source->last4;
- $paymentMethod->expiration = $source->expirationYear . '-' . $source->expirationMonth . '-01';
- } elseif ($source instanceof \Braintree\PayPalAccount) {
- $paymentMethod->email = $source->email;
- $paymentMethod->payment_type_id = PAYMENT_TYPE_ID_PAYPAL;
- } else {
- return null;
- }
-
- $paymentMethod->setRelation('payment_type', Cache::get('paymentTypes')->find($paymentMethod->payment_type_id));
-
- $paymentMethod->source_reference = $source->token;
-
- return $paymentMethod;
- }
-
public function convertPaymentMethodFromWePay($source, $accountGatewayToken = null, $paymentMethod = null) {
// Creating a new one or updating an existing one
if (!$paymentMethod) {
@@ -554,47 +149,7 @@ class PaymentService extends BaseService
}
public function convertPaymentMethodFromGatewayResponse($gatewayResponse, $accountGateway, $accountGatewayToken = null, $contactId = null, $existingPaymentMethod = null) {
- if ($accountGateway->gateway_id == GATEWAY_STRIPE) {
- $data = $gatewayResponse->getData();
- if (!empty($data['object']) && ($data['object'] == 'card' || $data['object'] == 'bank_account')) {
- $source = $data;
- } elseif (!empty($data['object']) && $data['object'] == 'customer') {
- $sources = !empty($data['sources']) ? $data['sources'] : $data['cards'];
- $source = reset($sources['data']);
- } else {
- $source = !empty($data['source']) ? $data['source'] : $data['card'];
- }
-
- if ($source) {
- $paymentMethod = $this->convertPaymentMethodFromStripe($source, $accountGatewayToken, $existingPaymentMethod);
- }
- } elseif ($accountGateway->gateway_id == GATEWAY_BRAINTREE) {
- $data = $gatewayResponse->getData();
-
- if (!empty($data->transaction)) {
- $transaction = $data->transaction;
-
- if ($existingPaymentMethod) {
- $paymentMethod = $existingPaymentMethod;
- } else {
- $paymentMethod = $accountGatewayToken ? PaymentMethod::createNew($accountGatewayToken) : new PaymentMethod();
- }
-
- if ($transaction->paymentInstrumentType == 'credit_card') {
- $card = $transaction->creditCardDetails;
- $paymentMethod->last4 = $card->last4;
- $paymentMethod->expiration = $card->expirationYear . '-' . $card->expirationMonth . '-01';
- $paymentMethod->payment_type_id = $this->parseCardType($card->cardType);
- } elseif ($transaction->paymentInstrumentType == 'paypal_account') {
- $paymentMethod->payment_type_id = PAYMENT_TYPE_ID_PAYPAL;
- $paymentMethod->email = $transaction->paypalDetails->payerEmail;
- }
- $paymentMethod->setRelation('payment_type', Cache::get('paymentTypes')->find($paymentMethod->payment_type_id));
- } elseif (!empty($data->paymentMethod)) {
- $paymentMethod = $this->convertPaymentMethodFromBraintree($data->paymentMethod, $accountGatewayToken, $existingPaymentMethod);
- }
-
- } elseif ($accountGateway->gateway_id == GATEWAY_WEPAY) {
+ if ($accountGateway->gateway_id == GATEWAY_WEPAY) {
if ($gatewayResponse instanceof \Omnipay\WePay\Message\CustomCheckoutResponse) {
$wepay = \Utils::setupWePay($accountGateway);
$paymentMethodType = $gatewayResponse->getData()['payment_method']['type'];
@@ -624,255 +179,27 @@ class PaymentService extends BaseService
return $paymentMethod;
}
- public function getCheckoutComToken($invitation)
- {
- $token = false;
- $invoice = $invitation->invoice;
- $client = $invoice->client;
- $account = $invoice->account;
-
- $accountGateway = $account->getGatewayConfig(GATEWAY_CHECKOUT_COM);
-
- $response = $this->purchase($accountGateway, [
- '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 getBraintreeClientToken($account)
- {
- $token = false;
-
- $accountGateway = $account->getGatewayConfig(GATEWAY_BRAINTREE);
- $gateway = $this->createGateway($accountGateway);
-
- $token = $gateway->clientToken()->send()->getToken();
-
- return $token;
- }
-
- public function createPayment($invitation, $accountGateway, $ref, $payerId = null, $paymentDetails = null, $paymentMethod = null, $purchaseResponse = null)
- {
- $invoice = $invitation->invoice;
-
- $payment = Payment::createNew($invitation);
- $payment->invitation_id = $invitation->id;
- $payment->account_gateway_id = $accountGateway->id;
- $payment->invoice_id = $invoice->id;
- $payment->amount = $invoice->getRequestedAmount();
- $payment->client_id = $invoice->client_id;
- $payment->contact_id = $invitation->contact_id;
- $payment->transaction_reference = $ref;
- $payment->payment_date = date_create()->format('Y-m-d');
-
- if (!empty($paymentDetails['card'])) {
- $card = $paymentDetails['card'];
- $payment->last4 = $card->getNumberLastFour();
- $payment->payment_type_id = $this->detectCardType($card->getNumber());
- }
-
- if (!empty($paymentDetails['ip'])) {
- $payment->ip = $paymentDetails['ip'];
- }
-
- $savePaymentMethod = !empty($paymentMethod);
-
- // This will convert various gateway's formats to a known format
- $paymentMethod = $this->convertPaymentMethodFromGatewayResponse($purchaseResponse, $accountGateway, null, null, $paymentMethod);
-
- // If this is a stored payment method, we'll update it with the latest info
- if ($savePaymentMethod) {
- $paymentMethod->save();
- }
-
- if ($accountGateway->gateway_id == GATEWAY_STRIPE) {
- $data = $purchaseResponse->getData();
- $payment->payment_status_id = $data['status'] == 'succeeded' ? PAYMENT_STATUS_COMPLETED : PAYMENT_STATUS_PENDING;
- }
-
- if ($paymentMethod) {
- if ($paymentMethod->last4) {
- $payment->last4 = $paymentMethod->last4;
- }
-
- if ($paymentMethod->expiration) {
- $payment->expiration = $paymentMethod->expiration;
- }
-
- if ($paymentMethod->routing_number) {
- $payment->routing_number = $paymentMethod->routing_number;
- }
-
- if ($paymentMethod->payment_type_id) {
- $payment->payment_type_id = $paymentMethod->payment_type_id;
- }
-
- if ($paymentMethod->email) {
- $payment->email = $paymentMethod->email;
- }
-
- if ($paymentMethod->bank_name) {
- $payment->bank_name = $paymentMethod->bank_name;
- }
-
- if ($payerId) {
- $payment->payer_id = $payerId;
- }
-
- if ($savePaymentMethod) {
- $payment->payment_method_id = $paymentMethod->id;
- }
- }
-
- $payment->save();
-
- // enable pro plan for hosted users
- if ($invoice->account->account_key == NINJA_ACCOUNT_KEY) {
- foreach ($invoice->invoice_items as $invoice_item) {
- // Hacky, but invoices don't have meta fields to allow us to store this easily
- if (1 == preg_match('/^Plan - (.+) \((.+)\)$/', $invoice_item->product_key, $matches)) {
- $plan = strtolower($matches[1]);
- $term = strtolower($matches[2]);
- } elseif ($invoice_item->product_key == 'Pending Monthly') {
- $pending_monthly = true;
- }
- }
-
- if (!empty($plan)) {
- $account = Account::with('users')->find($invoice->client->public_id);
-
- if(
- $account->company->plan != $plan
- || DateTime::createFromFormat('Y-m-d', $account->company->plan_expires) >= date_create('-7 days')
- ) {
- // Either this is a different plan, or the subscription expired more than a week ago
- // Reset any grandfathering
- $account->company->plan_started = date_create()->format('Y-m-d');
- }
-
- if (
- $account->company->plan == $plan
- && $account->company->plan_term == $term
- && DateTime::createFromFormat('Y-m-d', $account->company->plan_expires) >= date_create()
- ) {
- // This is a renewal; mark it paid as of when this term expires
- $account->company->plan_paid = $account->company->plan_expires;
- } else {
- $account->company->plan_paid = date_create()->format('Y-m-d');
- }
-
- $account->company->payment_id = $payment->id;
- $account->company->plan = $plan;
- $account->company->plan_term = $term;
- $account->company->plan_expires = DateTime::createFromFormat('Y-m-d', $account->company->plan_paid)
- ->modify($term == PLAN_TERM_MONTHLY ? '+1 month' : '+1 year')->format('Y-m-d');
-
- if (!empty($pending_monthly)) {
- $account->company->pending_plan = $plan;
- $account->company->pending_term = PLAN_TERM_MONTHLY;
- } else {
- $account->company->pending_plan = null;
- $account->company->pending_term = null;
- }
-
- $account->company->save();
- }
- }
-
- return $payment;
- }
-
- private function parseCardType($cardName) {
- $cardTypes = array(
- 'visa' => PAYMENT_TYPE_VISA,
- 'americanexpress' => PAYMENT_TYPE_AMERICAN_EXPRESS,
- 'amex' => PAYMENT_TYPE_AMERICAN_EXPRESS,
- 'mastercard' => PAYMENT_TYPE_MASTERCARD,
- 'discover' => PAYMENT_TYPE_DISCOVER,
- 'jcb' => PAYMENT_TYPE_JCB,
- 'dinersclub' => PAYMENT_TYPE_DINERS,
- 'carteblanche' => PAYMENT_TYPE_CARTE_BLANCHE,
- 'chinaunionpay' => PAYMENT_TYPE_UNIONPAY,
- 'unionpay' => PAYMENT_TYPE_UNIONPAY,
- 'laser' => PAYMENT_TYPE_LASER,
- 'maestro' => PAYMENT_TYPE_MAESTRO,
- 'solo' => PAYMENT_TYPE_SOLO,
- 'switch' => PAYMENT_TYPE_SWITCH
- );
-
- $cardName = strtolower(str_replace(array(' ', '-', '_'), '', $cardName));
-
- if (empty($cardTypes[$cardName]) && 1 == preg_match('/^('.implode('|', array_keys($cardTypes)).')/', $cardName, $matches)) {
- // Some gateways return extra stuff after the card name
- $cardName = $matches[1];
- }
-
- if (!empty($cardTypes[$cardName])) {
- return $cardTypes[$cardName];
- } else {
- return PAYMENT_TYPE_CREDIT_CARD_OTHER;
- }
- }
-
- private function detectCardType($number)
- {
- if (preg_match('/^3[47][0-9]{13}$/',$number)) {
- return PAYMENT_TYPE_AMERICAN_EXPRESS;
- } elseif (preg_match('/^3(?:0[0-5]|[68][0-9])[0-9]{11}$/',$number)) {
- return PAYMENT_TYPE_DINERS;
- } elseif (preg_match('/^6(?:011|5[0-9][0-9])[0-9]{12}$/',$number)) {
- return PAYMENT_TYPE_DISCOVER;
- } elseif (preg_match('/^(?:2131|1800|35\d{3})\d{11}$/',$number)) {
- return PAYMENT_TYPE_JCB;
- } elseif (preg_match('/^5[1-5][0-9]{14}$/',$number)) {
- return PAYMENT_TYPE_MASTERCARD;
- } elseif (preg_match('/^4[0-9]{12}(?:[0-9]{3})?$/',$number)) {
- return PAYMENT_TYPE_VISA;
- }
- return PAYMENT_TYPE_CREDIT_CARD_OTHER;
- }
-
- public function completePurchase($gateway, $accountGateway, $details, $token)
- {
- if ($accountGateway->isGateway(GATEWAY_MOLLIE)) {
- $details['transactionReference'] = $token;
- $response = $gateway->fetchTransaction($details)->send();
- return $gateway->fetchTransaction($details)->send();
- } else {
-
- return $gateway->completePurchase($details)->send();
- }
- }
public function autoBillInvoice($invoice)
{
$client = $invoice->client;
-
- // Make sure we've migrated in data from Stripe
- $this->getClientPaymentMethods($client);
-
+ $account = $client->account;
$invitation = $invoice->invitations->first();
- $token = $client->getGatewayToken($accountGateway/* return parameter */, $accountGatewayToken/* return parameter */);
- if (!$accountGatewayToken) {
+ if ( ! $invitation) {
return false;
}
- $defaultPaymentMethod = $accountGatewayToken->default_payment_method;
+ $paymentDriver = $account->paymentDriver($invitation, GATEWAY_TYPE_TOKEN);
+ $customer = $paymentDriver->customer();
- if (!$invitation || !$token || !$defaultPaymentMethod) {
+ if ( ! $customer) {
return false;
}
- if ($defaultPaymentMethod->requiresDelayedAutoBill()) {
+ $paymentMethod = $customer->default_payment_method;
+
+ if ($paymentMethod->requiresDelayedAutoBill()) {
$invoiceDate = \DateTime::createFromFormat('Y-m-d', $invoice->invoice_date);
$minDueDate = clone $invoiceDate;
$minDueDate->modify('+10 days');
@@ -906,43 +233,14 @@ class PaymentService extends BaseService
}
}
- // setup the gateway/payment info
- $details = $this->getPaymentDetails($invitation, $accountGateway);
- $details['customerReference'] = $token;
- $details['token'] = $defaultPaymentMethod->source_reference;
- $details['paymentType'] = $defaultPaymentMethod->payment_type_id;
+ return $paymentDriver->completeOnsitePurchase(false, $paymentMethod);
+
+ /*
if ($accountGateway->gateway_id == GATEWAY_WEPAY) {
$details['transaction_id'] = 'autobill_'.$invoice->id;
}
-
- // submit purchase/get response
- $response = $this->purchase($accountGateway, $details);
-
- if ($response->isSuccessful()) {
- $ref = $response->getTransactionReference();
- return $this->createPayment($invitation, $accountGateway, $ref, null, $details, $defaultPaymentMethod, $response);
- } else {
- return false;
- }
- }
-
- public function getClientDefaultPaymentMethod($client) {
- $this->getClientPaymentMethods($client);
-
- $client->getGatewayToken($accountGateway/* return parameter */, $accountGatewayToken/* return parameter */);
-
- if (!$accountGatewayToken) {
- return false;
- }
-
- return $accountGatewayToken->default_payment_method;
- }
-
- public function getClientRequiresDelayedAutoBill($client) {
- $defaultPaymentMethod = $this->getClientDefaultPaymentMethod($client);
-
- return $defaultPaymentMethod?$defaultPaymentMethod->requiresDelayedAutoBill():null;
+ */
}
public function getDatatable($clientPublicId, $search)
@@ -969,9 +267,11 @@ class PaymentService extends BaseService
$successful = 0;
foreach ($payments as $payment) {
- if(Auth::user()->can('edit', $payment)){
+ if (Auth::user()->can('edit', $payment)) {
$amount = !empty($params['amount']) ? floatval($params['amount']) : null;
- if ($this->refund($payment, $amount)) {
+ $accountGateway = $payment->account_gateway;
+ $paymentDriver = $accountGateway->paymentDriver();
+ if ($paymentDriver->refundPayment($payment, $amount)) {
$successful++;
}
}
@@ -983,201 +283,4 @@ class PaymentService extends BaseService
}
}
- public function refund($payment, $amount = null) {
- if ($amount) {
- $amount = min($amount, $payment->amount - $payment->refunded);
- }
-
- $accountGateway = $payment->account_gateway;
-
- if (!$accountGateway) {
- $accountGateway = AccountGateway::withTrashed()->find($payment->account_gateway_id);
- }
-
- if (!$amount || !$accountGateway) {
- return;
- }
-
- if ($payment->payment_type_id != PAYMENT_TYPE_CREDIT) {
- $gateway = $this->createGateway($accountGateway);
-
- $details = array(
- 'transactionReference' => $payment->transaction_reference,
- );
-
- if ($accountGateway->gateway_id == GATEWAY_WEPAY && $amount == $payment->getCompletedAmount()) {
- // WePay issues a full refund when no amount is set. If an amount is set, it will try
- // to issue a partial refund without refunding any fees. However, the Stripe driver
- // (but not the API) requires the amount parameter to be set no matter what.
- } else {
- $details['amount'] = $amount;
- }
-
- if ($accountGateway->gateway_id == GATEWAY_WEPAY) {
- $details['refund_reason'] = 'Refund issued by merchant.';
- }
-
- $refund = $gateway->refund($details);
- $response = $refund->send();
-
- if ($response->isSuccessful()) {
- $payment->recordRefund($amount);
- } else {
- $data = $response->getData();
-
- if ($data instanceof \Braintree\Result\Error) {
- $error = $data->errors->deepAll()[0];
- if ($error && $error->code == 91506) {
- $tryVoid = true;
- }
- } elseif ($accountGateway->gateway_id == GATEWAY_WEPAY && $response->getCode() == 4004) {
- $tryVoid = true;
- }
-
- if (!empty($tryVoid)) {
- if ($amount == $payment->amount) {
- // This is an unsettled transaction; try to void it
- $void = $gateway->void(array(
- 'transactionReference' => $payment->transaction_reference,
- ));
- $response = $void->send();
-
- if ($response->isSuccessful()) {
- $payment->markVoided();
- }
- } else {
- $this->error('Unknown', 'Partial refund not allowed for unsettled transactions.', $accountGateway);
- return false;
- }
- }
-
- if (!$response->isSuccessful()) {
- $this->error('Unknown', $response->getMessage(), $accountGateway);
- return false;
- }
- }
- } else {
- $payment->recordRefund($amount);
- }
- return true;
- }
-
- private function error($type, $error, $accountGateway = false, $exception = false)
- {
- $message = '';
- if ($accountGateway && $accountGateway->gateway) {
- $message = $accountGateway->gateway->name . ': ';
- }
- $message .= $error ?: trans('texts.payment_error');
-
- Session::flash('error', $message);
- Utils::logError("Payment Error [{$type}]: " . ($exception ? Utils::getErrorString($exception) : $message), 'PHP', true);
- }
-
- public function makeStripeCall($accountGateway, $method, $url, $body = null) {
- $apiKey = $accountGateway->getConfig()->apiKey;
-
- if (!$apiKey) {
- return 'No API key set';
- }
-
- try{
- $options = [
- 'headers' => ['content-type' => 'application/x-www-form-urlencoded'],
- 'auth' => [$accountGateway->getConfig()->apiKey,''],
- ];
-
- if ($body) {
- $options['body'] = $body;
- }
-
- $response = (new \GuzzleHttp\Client(['base_uri'=>'https://api.stripe.com/v1/']))->request(
- $method,
- $url,
- $options
- );
- return json_decode($response->getBody(), true);
- } catch (\GuzzleHttp\Exception\BadResponseException $e) {
- $response = $e->getResponse();
- $body = json_decode($response->getBody(), true);
-
- if ($body && $body['error'] && $body['error']['type'] == 'invalid_request_error') {
- return $body['error']['message'];
- }
-
- return $e->getMessage();
- }
- }
-
- private function getPlaidToken($accountGateway, $publicToken, $accountId) {
- $clientId = $accountGateway->getPlaidClientId();
- $secret = $accountGateway->getPlaidSecret();
-
- if (!$clientId) {
- return 'No client ID set';
- }
-
- if (!$secret) {
- return 'No secret set';
- }
-
- try{
- $subdomain = $accountGateway->getPlaidEnvironment() == 'production' ? 'api' : 'tartan';
- $response = (new \GuzzleHttp\Client(['base_uri'=>"https://{$subdomain}.plaid.com"]))->request(
- 'POST',
- 'exchange_token',
- [
- 'allow_redirects' => false,
- 'headers' => ['content-type' => 'application/x-www-form-urlencoded'],
- 'body' => http_build_query(array(
- 'client_id' => $clientId,
- 'secret' => $secret,
- 'public_token' => $publicToken,
- 'account_id' => $accountId,
- ))
- ]
- );
- return json_decode($response->getBody(), true);
- } catch (\GuzzleHttp\Exception\BadResponseException $e) {
- $response = $e->getResponse();
- $body = json_decode($response->getBody(), true);
-
- if ($body && !empty($body['message'])) {
- return $body['message'];
- }
-
- return $e->getMessage();
- }
- }
-
- public function purchase($accountGateway, $details) {
- $gateway = $this->createGateway($accountGateway);
-
- if ($accountGateway->gateway_id == GATEWAY_WEPAY) {
- $details['applicationFee'] = $this->calculateApplicationFee($accountGateway, $details['amount']);
- $details['feePayer'] = WEPAY_FEE_PAYER;
- $details['callbackUri'] = $accountGateway->getWebhookUrl();
- if(isset($details['paymentType'])) {
- if($details['paymentType'] == PAYMENT_TYPE_ACH || $details['paymentType'] == PAYMENT_TYPE_WEPAY_ACH) {
- $details['paymentMethodType'] = 'payment_bank';
- }
-
- unset($details['paymentType']);
- }
- }
-
- $response = $gateway->purchase($details)->send();
-
- return $response;
- }
-
- private function calculateApplicationFee($accountGateway, $amount) {
- if ($accountGateway->gateway_id = GATEWAY_WEPAY) {
- $fee = WEPAY_APP_FEE_MULTIPLIER * $amount + WEPAY_APP_FEE_FIXED;
-
- return floor(min($fee, $amount * 0.2));// Maximum fee is 20% of the amount.
- }
-
- return 0;
- }
}
diff --git a/app/Services/TemplateService.php b/app/Services/TemplateService.php
index cc3e1becc031..564eb76da6dd 100644
--- a/app/Services/TemplateService.php
+++ b/app/Services/TemplateService.php
@@ -28,7 +28,7 @@ class TemplateService
}
$documentsHTML .= '';
}
-
+
$variables = [
'$footer' => $account->getEmailFooter(),
'$client' => $client->getDisplayName(),
@@ -55,15 +55,14 @@ class TemplateService
];
// Add variables for available payment types
- foreach (Gateway::$paymentTypes as $type) {
- $camelType = Gateway::getPaymentTypeName($type);
- $type = Utils::toSnakeCase($camelType);
+ foreach (Gateway::$gatewayTypes as $type) {
+ $camelType = Utils::toCamelCase($type);
$variables["\${$camelType}Link"] = $invitation->getLink('payment') . "/{$type}";
$variables["\${$camelType}Button"] = Form::emailPaymentButton($invitation->getLink('payment') . "/{$type}");
}
-
+
$includesPasswordPlaceholder = strpos($template, '$password') !== false;
-
+
$str = str_replace(array_keys($variables), array_values($variables), $template);
if (!$includesPasswordPlaceholder && $passwordHTML) {
@@ -72,10 +71,10 @@ class TemplateService
{
$str = substr_replace($str, $passwordHTML, $pos, 9/* length of "$password" */);
}
- }
+ }
$str = str_replace('$password', '', $str);
$str = autolink($str, 100);
-
+
return $str;
- }
-}
\ No newline at end of file
+ }
+}
diff --git a/database/migrations/2016_04_23_182223_payments_changes.php b/database/migrations/2016_04_23_182223_payments_changes.php
index 35a1b6134e9b..b565b1dc752a 100644
--- a/database/migrations/2016_04_23_182223_payments_changes.php
+++ b/database/migrations/2016_04_23_182223_payments_changes.php
@@ -28,6 +28,7 @@ class PaymentsChanges extends Migration
{
$table->increments('id');
$table->unsignedInteger('account_id');
+ $table->unsignedInteger('user_id');
$table->unsignedInteger('contact_id')->nullable();
$table->unsignedInteger('account_gateway_token_id');
$table->unsignedInteger('payment_type_id');
@@ -44,6 +45,7 @@ class PaymentsChanges extends Migration
$table->softDeletes();
$table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
+ $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->foreign('contact_id')->references('id')->on('contacts')->onDelete('cascade');
$table->foreign('account_gateway_token_id')->references('id')->on('account_gateway_tokens');
$table->foreign('payment_type_id')->references('id')->on('payment_types');
@@ -107,7 +109,7 @@ class PaymentsChanges extends Migration
$table->dropColumn('refunded');
$table->dropForeign('payments_payment_status_id_foreign');
$table->dropColumn('payment_status_id');
-
+
$table->dropColumn('routing_number');
$table->dropColumn('last4');
$table->dropColumn('expiration');
@@ -139,7 +141,7 @@ class PaymentsChanges extends Migration
Schema::table('invoices', function ($table) {
$table->dropColumn('client_enable_auto_bill');
});
-
+
Schema::dropIfExists('payment_statuses');
Schema::table('account_gateway_tokens', function($table)
diff --git a/database/seeds/PaymentLibrariesSeeder.php b/database/seeds/PaymentLibrariesSeeder.php
index 88a270930fd4..280db905d3bb 100644
--- a/database/seeds/PaymentLibrariesSeeder.php
+++ b/database/seeds/PaymentLibrariesSeeder.php
@@ -16,18 +16,18 @@ class PaymentLibrariesSeeder extends Seeder
$gateways = [
['name' => 'Authorize.Net AIM', 'provider' => 'AuthorizeNet_AIM'],
- ['name' => 'Authorize.Net SIM', 'provider' => 'AuthorizeNet_SIM'],
+ ['name' => 'Authorize.Net SIM', 'provider' => 'AuthorizeNet_SIM', 'payment_library_id' => 2],
['name' => 'CardSave', 'provider' => 'CardSave'],
- ['name' => 'Eway Rapid', 'provider' => 'Eway_Rapid'],
+ ['name' => 'Eway Rapid', 'provider' => 'Eway_Rapid', 'is_offsite' => true],
['name' => 'FirstData Connect', 'provider' => 'FirstData_Connect'],
['name' => 'GoCardless', 'provider' => 'GoCardless', 'is_offsite' => true],
['name' => 'Migs ThreeParty', 'provider' => 'Migs_ThreeParty'],
['name' => 'Migs TwoParty', 'provider' => 'Migs_TwoParty'],
- ['name' => 'Mollie', 'provider' => 'Mollie'],
+ ['name' => 'Mollie', 'provider' => 'Mollie', 'is_offsite' => true],
['name' => 'MultiSafepay', 'provider' => 'MultiSafepay'],
['name' => 'Netaxept', 'provider' => 'Netaxept'],
['name' => 'NetBanx', 'provider' => 'NetBanx'],
- ['name' => 'PayFast', 'provider' => 'PayFast'],
+ ['name' => 'PayFast', 'provider' => 'PayFast', 'is_offsite' => true],
['name' => 'Payflow Pro', 'provider' => 'Payflow_Pro'],
['name' => 'PaymentExpress PxPay', 'provider' => 'PaymentExpress_PxPay'],
['name' => 'PaymentExpress PxPost', 'provider' => 'PaymentExpress_PxPost'],
@@ -41,7 +41,7 @@ class PaymentLibrariesSeeder extends Seeder
['name' => 'TargetPay Direct eBanking', 'provider' => 'TargetPay_Directebanking'],
['name' => 'TargetPay Ideal', 'provider' => 'TargetPay_Ideal'],
['name' => 'TargetPay Mr Cash', 'provider' => 'TargetPay_Mrcash'],
- ['name' => 'TwoCheckout', 'provider' => 'TwoCheckout'],
+ ['name' => 'TwoCheckout', 'provider' => 'TwoCheckout', 'is_offsite' => true],
['name' => 'WorldPay', 'provider' => 'WorldPay'],
['name' => 'BeanStream', 'provider' => 'BeanStream', 'payment_library_id' => 2],
['name' => 'Psigate', 'provider' => 'Psigate', 'payment_library_id' => 2],
diff --git a/database/seeds/UserTableSeeder.php b/database/seeds/UserTableSeeder.php
index 1d0759dbc609..990c7c53ae6f 100644
--- a/database/seeds/UserTableSeeder.php
+++ b/database/seeds/UserTableSeeder.php
@@ -68,7 +68,8 @@ class UserTableSeeder extends Seeder
'city' => $faker->city,
'state' => $faker->state,
'postal_code' => $faker->postcode,
- 'country_id' => Country::all()->random()->id,
+ 'country_id' => DEFAULT_COUNTRY,
+ 'currency_id' => DEFAULT_CURRENCY,
]);
Contact::create([
@@ -77,6 +78,7 @@ class UserTableSeeder extends Seeder
'client_id' => $client->id,
'public_id' => 1,
'email' => TEST_USERNAME,
+ 'is_primary' => true,
]);
Product::create([
diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php
index 92552e8878d0..58efc4ecb53d 100644
--- a/resources/lang/en/texts.php
+++ b/resources/lang/en/texts.php
@@ -445,7 +445,7 @@ $LANG = array(
'token_billing_4' => 'Always',
'token_billing_checkbox' => 'Store credit card details',
'view_in_gateway' => 'View in :gateway',
- 'use_card_on_file' => 'Use card on file',
+ 'use_card_on_file' => 'Use Card on File',
'edit_payment_details' => 'Edit payment details',
'token_billing' => 'Save card details',
'token_billing_secure' => 'The data is stored securely by :link',
@@ -1351,6 +1351,12 @@ $LANG = array(
'update_font_cache' => 'Please force refresh the page to update the font cache.',
'more_options' => 'More options',
+ 'credit_card' => 'Credit Card',
+ 'bank_transfer' => 'Bank Transfer',
+ 'no_transaction_reference' => 'We did not recieve a payment transaction reference from the gateway.',
+ 'use_bank_on_file' => 'Use Bank on File',
+ 'auto_bill_email_message' => 'This invoice will automatically be billed to the payment method on file on the due date.',
+ 'bitcoin' => 'Bitcoin',
);
diff --git a/resources/views/accounts/account_gateway.blade.php b/resources/views/accounts/account_gateway.blade.php
index c14255737fc7..093fdef59ddb 100644
--- a/resources/views/accounts/account_gateway.blade.php
+++ b/resources/views/accounts/account_gateway.blade.php
@@ -13,8 +13,7 @@
{!! Former::open($url)->method($method)->rule()->addClass('warn-on-exit') !!}
@if ($accountGateway)
- {!! Former::populateField('gateway_id', $accountGateway->gateway_id) !!}
- {!! Former::populateField('payment_type_id', $paymentTypeId) !!}
+ {!! Former::populateField('primary_gateway_id', $accountGateway->gateway_id) !!}
{!! Former::populateField('recommendedGateway_id', $accountGateway->gateway_id) !!}
{!! Former::populateField('show_address', intval($accountGateway->show_address)) !!}
{!! Former::populateField('update_address', intval($accountGateway->update_address)) !!}
@@ -39,14 +38,13 @@
{!! Former::populateField('update_address', 1) !!}
@if (Utils::isNinjaDev())
- {!! Former::populateField('23_apiKey', env('STRIPE_TEST_SECRET_KEY')) !!}
- {!! Former::populateField('publishable_key', env('STRIPE_TEST_PUBLISHABLE_KEY')) !!}
+ @include('accounts.partials.payment_credentials')
@endif
@endif
@if ($accountGateway)
- {!! Former::text('primary_gateway_id')->value($accountGateway->gateway_id) !!}
+ {!! Former::text('primary_gateway_id') !!}
@else
{!! Former::select('primary_gateway_id')
@@ -168,7 +166,7 @@
@endif
-
+
@@ -203,6 +201,12 @@
} else {
$('.onsite-fields').show();
}
+
+ if (gateway.id == {{ GATEWAY_STRIPE }}) {
+ $('.stripe-ach').show();
+ } else {
+ $('.stripe-ach').hide();
+ }
}
function gatewayLink(url) {
diff --git a/resources/views/accounts/account_gateway_wepay.blade.php b/resources/views/accounts/account_gateway_wepay.blade.php
index 9adcdb5be2f2..00367af2de8a 100644
--- a/resources/views/accounts/account_gateway_wepay.blade.php
+++ b/resources/views/accounts/account_gateway_wepay.blade.php
@@ -62,18 +62,9 @@
->label('Accepted Credit Cards')
->checkboxes($creditCardTypes)
->class('creditcard-types') !!}
- @if ($account->getGatewayByType(PAYMENT_TYPE_DIRECT_DEBIT))
- {!! Former::checkbox('enable_ach')
- ->label(trans('texts.ach'))
- ->text(trans('texts.enable_ach'))
- ->value(null)
- ->disabled(true)
- ->help(trans('texts.ach_disabled')) !!}
- @else
- {!! Former::checkbox('enable_ach')
+ {!! Former::checkbox('enable_ach')
->label(trans('texts.ach'))
->text(trans('texts.enable_ach')) !!}
- @endif
{!! Former::checkbox('tos_agree')->label(' ')->text(trans('texts.wepay_tos_agree',
['link'=>''.trans('texts.wepay_tos_link_text').'']
@@ -83,7 +74,7 @@
- {!! Button::normal(trans('texts.use_another_provider'))->large()->asLinkTo(URL::to('/gateways/create/0')) !!}
+ {!! Button::normal(trans('texts.use_another_provider'))->large()->asLinkTo(URL::to('/gateways/create?other_providers=true')) !!}
{!! Button::success(trans('texts.sign_up_with_wepay'))->submit()->large() !!}
@@ -108,7 +99,7 @@
})
-
+
{!! Former::close() !!}
@stop
diff --git a/resources/views/accounts/partials/payment_credentials.blade.php b/resources/views/accounts/partials/payment_credentials.blade.php
new file mode 100644
index 000000000000..b2e641f4e0a1
--- /dev/null
+++ b/resources/views/accounts/partials/payment_credentials.blade.php
@@ -0,0 +1,47 @@
+{!! Former::populateField(GATEWAY_STRIPE . '_apiKey', env('STRIPE_TEST_SECRET_KEY')) !!}
+{!! Former::populateField('publishable_key', env('STRIPE_TEST_PUBLISHABLE_KEY')) !!}
+{!! Former::populateField('enable_ach', 1) !!}
+{!! Former::populateField('plaid_client_id', env('PLAID_TEST_CLIENT_ID')) !!}
+{!! Former::populateField('plaid_secret', env('PLAID_TEST_SECRET')) !!}
+{!! Former::populateField('plaid_public_key', env('PLAID_TEST_PUBLIC_KEY')) !!}
+
+{!! Former::populateField(GATEWAY_PAYPAL_EXPRESS . '_username', env('PAYPAL_TEST_USERNAME')) !!}
+{!! Former::populateField(GATEWAY_PAYPAL_EXPRESS . '_password', env('PAYPAL_TEST_PASSWORD')) !!}
+{!! Former::populateField(GATEWAY_PAYPAL_EXPRESS . '_signature', env('PAYPAL_TEST_SIGNATURE')) !!}
+{!! Former::populateField(GATEWAY_PAYPAL_EXPRESS . '_testMode', 1) !!}
+
+{!! Former::populateField(GATEWAY_DWOLLA . '_destinationId', env('DWOLLA_TEST_DESTINATION_ID')) !!}
+
+{!! Former::populateField(GATEWAY_BITPAY . '_testMode', 1) !!}
+{!! Former::populateField(GATEWAY_BITPAY . '_apiKey', env('BITPAY_TEST_API_KEY')) !!}
+
+{!! Former::populateField(GATEWAY_TWO_CHECKOUT . '_secretWord', env('TWO_CHECKOUT_SECRET_WORD')) !!}
+{!! Former::populateField(GATEWAY_TWO_CHECKOUT . '_accountNumber', env('TWO_CHECKOUT_ACCOUNT_NUMBER')) !!}
+{!! Former::populateField(GATEWAY_TWO_CHECKOUT . '_testMode', 1) !!}
+
+{!! Former::populateField(GATEWAY_MOLLIE . '_apiKey', env('MOLLIE_TEST_API_KEY')) !!}
+
+{!! Former::populateField(GATEWAY_AUTHORIZE_NET . '_apiLoginId', env('AUTHORIZE_NET_TEST_API_LOGIN_ID')) !!}
+{!! Former::populateField(GATEWAY_AUTHORIZE_NET . '_transactionKey', env('AUTHORIZE_NET_TEST_TRANSACTION_KEY')) !!}
+{!! Former::populateField(GATEWAY_AUTHORIZE_NET . '_secretKey', env('AUTHORIZE_NET_TEST_SECRET_KEY')) !!}
+{!! Former::populateField(GATEWAY_AUTHORIZE_NET . '_testMode', 1) !!}
+{!! Former::populateField(GATEWAY_AUTHORIZE_NET . '_developerMode', 1) !!}
+
+{!! Former::populateField(GATEWAY_CHECKOUT_COM . '_publicApiKey', env('CHECKOUT_COM_TEST_PUBLIC_API_KEY')) !!}
+{!! Former::populateField(GATEWAY_CHECKOUT_COM . '_secretApiKey', env('CHECKOUT_COM_TEST_SECRET_API_KEY')) !!}
+{!! Former::populateField(GATEWAY_CHECKOUT_COM . '_testMode', 1) !!}
+
+{!! Former::populateField(GATEWAY_CYBERSOURCE . '_accessKey', env('CYBERSOURCE_TEST_ACCESS_KEY')) !!}
+{!! Former::populateField(GATEWAY_CYBERSOURCE . '_secretKey', env('CYBERSOURCE_TEST_SECRET_KEY')) !!}
+{!! Former::populateField(GATEWAY_CYBERSOURCE . '_profileId', env('CYBERSOURCE_TEST_PROFILE_ID')) !!}
+{!! Former::populateField(GATEWAY_CYBERSOURCE . '_testMode', 1) !!}
+
+{!! Former::populateField(GATEWAY_EWAY . '_apiKey', env('EWAY_TEST_API_KEY')) !!}
+{!! Former::populateField(GATEWAY_EWAY . '_password', env('EWAY_TEST_PASSWORD')) !!}
+{!! Former::populateField(GATEWAY_EWAY . '_testMode', 1) !!}
+
+{!! Former::populateField(GATEWAY_BRAINTREE . '_privateKey', env('BRAINTREE_TEST_PRIVATE_KEY')) !!}
+{!! Former::populateField(GATEWAY_BRAINTREE . '_publicKey', env('BRAINTREE_TEST_PUBLIC_KEY')) !!}
+{!! Former::populateField(GATEWAY_BRAINTREE . '_merchantId', env('BRAINTREE_TEST_MERCHANT_ID')) !!}
+{!! Former::populateField(GATEWAY_BRAINTREE . '_testMode', 1) !!}
+{!! Former::populateField('enable_paypal', 1) !!}
diff --git a/resources/views/accounts/payments.blade.php b/resources/views/accounts/payments.blade.php
index 55e7177c683c..5bb401c274cb 100644
--- a/resources/views/accounts/payments.blade.php
+++ b/resources/views/accounts/payments.blade.php
@@ -31,11 +31,13 @@
{!! Former::close() !!}
+
+
@if ($showAdd)
{!! Button::primary(trans('texts.add_gateway'))
->asLinkTo(URL::to('/gateways/create'))
@@ -48,14 +50,13 @@
{!! Datatable::table()
->addColumn(
trans('texts.name'),
- trans('texts.payment_type_id'),
trans('texts.action'))
->setUrl(url('api/gateways/'))
->setOptions('sPaginationType', 'bootstrap')
->setOptions('bFilter', false)
->setOptions('bAutoWidth', false)
- ->setOptions('aoColumns', [[ "sWidth"=> "50%" ], [ "sWidth"=> "30%" ], ["sWidth"=> "20%"]])
- ->setOptions('aoColumnDefs', [['bSortable'=>false, 'aTargets'=>[2]]])
+ ->setOptions('aoColumns', [[ "sWidth"=> "80%" ], ["sWidth"=> "20%"]])
+ ->setOptions('aoColumnDefs', [['bSortable'=>false, 'aTargets'=>[1]]])
->render('datatable') !!}
- @endif
-@stop
-
-@section('content')
-
- @include('payments.payment_css')
-
-
- @if($paymentType == PAYMENT_TYPE_STRIPE_ACH)
- {!! Former::open($url)
- ->autocomplete('on')
- ->addClass('payment-form')
- ->id('payment-form')
- ->rules(array(
- 'first_name' => 'required',
- 'last_name' => 'required',
- 'account_number' => 'required',
- 'routing_number' => 'required',
- 'account_holder_name' => 'required',
- 'account_holder_type' => 'required',
- 'authorize_ach' => 'required',
- )) !!}
- @else
- {!! Former::vertical_open($url)
- ->autocomplete('on')
- ->addClass('payment-form')
- ->id('payment-form')
- ->rules(array(
- 'first_name' => 'required',
- 'last_name' => 'required',
- 'card_number' => 'required',
- 'expiration_month' => 'required',
- 'expiration_year' => 'required',
- 'cvv' => 'required',
- 'address1' => 'required',
- 'city' => 'required',
- 'state' => 'required',
- 'postal_code' => 'required',
- 'country_id' => 'required',
- 'phone' => 'required',
- 'email' => 'required|email',
- 'authorize_ach' => 'required',
- 'tos_agree' => 'required',
- )) !!}
- @endif
-
- @if ($client)
- {{ Former::populate($client) }}
- {{ Former::populateField('first_name', $contact->first_name) }}
- {{ Former::populateField('last_name', $contact->last_name) }}
- {{ Former::populateField('email', $contact->email) }}
- @if (!$client->country_id && $client->account->country_id)
- {{ Former::populateField('country_id', $client->account->country_id) }}
- @endif
- @if (!$client->currency_id && $client->account->currency_id)
- {{ Former::populateField('currency_id', $client->account->currency_id) }}
- {{ Former::populateField('currency', $client->account->currency->code) }}
- @endif
- @endif
-
- @if (Utils::isNinjaDev())
- {{ Former::populateField('first_name', 'Test') }}
- {{ Former::populateField('last_name', 'Test') }}
- {{ Former::populateField('address1', '350 5th Ave') }}
- {{ Former::populateField('city', 'New York') }}
- {{ Former::populateField('state', 'NY') }}
- {{ Former::populateField('postal_code', '10118') }}
- {{ Former::populateField('country_id', 840) }}
-
-
- @endif
-
-
-
-
-
-
-
-
-
-
-
- @if ($client && isset($invoiceNumber))
- {{ $client->getDisplayName() }}
- {{ trans('texts.invoice') . ' ' . $invoiceNumber }}| {{ trans('texts.amount_due') }}: {{ $account->formatMoney($amount, $client, true) }}
- @elseif ($paymentTitle)
- {{ $paymentTitle }}
- @if(isset($paymentSubtitle))
-
{{ $paymentSubtitle }}
- @endif
-
- @endif
-
-
-
- @if (Request::secure() || Utils::isNinjaDev())
-
-
{{ trans('texts.secure_payment') }}
-
{{ trans('texts.256_encryption') }}
-
- @endif
-
-
-
-
-
- @if($paymentType != PAYMENT_TYPE_STRIPE_ACH && $paymentType != PAYMENT_TYPE_BRAINTREE_PAYPAL && $paymentType != PAYMENT_TYPE_WEPAY_ACH)
-
{{ trans('texts.contact_information') }}
-
-
- {!! Former::text('first_name')
- ->placeholder(trans('texts.first_name'))
- ->label('') !!}
-
-
- {!! Former::text('last_name')
- ->placeholder(trans('texts.last_name'))
- ->autocomplete('family-name')
- ->label('') !!}
-
-
- @if (isset($paymentTitle) || ! empty($contact->email))
-
-
- {!! Former::text('email')
- ->placeholder(trans('texts.email'))
- ->autocomplete('email')
- ->label('') !!}
-
-
- @endif
-
-
-
- @if (!empty($showAddress))
-
{{ trans('texts.billing_address') }} {{ trans('texts.payment_footer1') }}
-
-
- {!! Former::text('address1')
- ->autocomplete('address-line1')
- ->placeholder(trans('texts.address1'))
- ->label('') !!}
-
-
- {!! Former::text('address2')
- ->autocomplete('address-line2')
- ->placeholder(trans('texts.address2'))
- ->label('') !!}
-
-
-
-
- {!! Former::text('city')
- ->autocomplete('address-level2')
- ->placeholder(trans('texts.city'))
- ->label('') !!}
-
-
- {!! Former::text('state')
- ->autocomplete('address-level1')
- ->placeholder(trans('texts.state'))
- ->label('') !!}
-
-
-
-
- {!! Former::text('postal_code')
- ->autocomplete('postal-code')
- ->placeholder(trans('texts.postal_code'))
- ->label('') !!}
-
-
- {!! Former::select('country_id')
- ->placeholder(trans('texts.country_id'))
- ->fromQuery($countries, 'name', 'id')
- ->addGroupClass('country-select')
- ->label('') !!}
-
-
-
-
- @endif
-
-
{{ trans('texts.billing_method') }}
- @endif
-
-
- @if($paymentType == PAYMENT_TYPE_STRIPE_ACH)
- @if($accountGateway->getPlaidEnabled())
-
- @endif
-
- @if($accountGateway->getPlaidEnabled())
-
{{ trans('texts.or') }}
-
{{ trans('texts.link_manually') }}
- @endif
-
{{ trans('texts.ach_verification_delay_help') }}
- {!! Former::radios('account_holder_type')->radios(array(
- trans('texts.individual_account') => array('value' => 'individual'),
- trans('texts.company_account') => array('value' => 'company'),
- ))->inline()->label(trans('texts.account_holder_type')); !!}
- {!! Former::text('account_holder_name')
- ->label(trans('texts.account_holder_name')) !!}
- {!! Former::select('country_id')
- ->label(trans('texts.country_id'))
- ->fromQuery($countries, 'name', 'id')
- ->addGroupClass('country-select') !!}
- {!! Former::select('currency')
- ->label(trans('texts.currency_id'))
- ->fromQuery($currencies, 'name', 'code')
- ->addGroupClass('currency-select') !!}
- {!! Former::text('')
- ->id('routing_number')
- ->label(trans('texts.routing_number')) !!}
-
- {!! Former::text('')
- ->id('account_number')
- ->label(trans('texts.account_number')) !!}
- {!! Former::text('')
- ->id('confirm_account_number')
- ->label(trans('texts.confirm_account_number')) !!}
-
- {!! Former::checkbox('authorize_ach')
- ->text(trans('texts.ach_authorization', ['company'=>$account->getDisplayName(), 'email' => $account->work_email]))
- ->label(' ') !!}
-
- {!! Button::success(strtoupper(trans('texts.add_account')))
- ->submit()
- ->withAttributes(['id'=>'add_account_button'])
- ->large() !!}
- @if($accountGateway->getPlaidEnabled() && !empty($amount))
- {!! Button::success(strtoupper(trans('texts.pay_now') . ' - ' . $account->formatMoney($amount, $client, true) ))
- ->submit()
- ->withAttributes(['style'=>'display:none', 'id'=>'pay_now_button'])
- ->large() !!}
- @endif
-
- @elseif($paymentType == PAYMENT_TYPE_BRAINTREE_PAYPAL)
-
{{ trans('texts.paypal') }}
-
{{$details->firstName}} {{$details->lastName}}
-
{{$details->email}}
-
-
-
-
-
- @if (isset($amount) && $client && $account->showTokenCheckbox())
-
selectTokenCheckbox() ? 'CHECKED' : '' }} value="1" style="margin-left:0px; vertical-align:top">
-
-
- {!! trans('texts.token_billing_secure', ['link' => link_to('https://www.braintreepayments.com/', 'Braintree', ['target' => '_blank'])]) !!}
-
- @endif
-
-
- @if(isset($amount))
- {!! Button::success(strtoupper(trans('texts.pay_now') . ' - ' . $account->formatMoney($amount, $client, true) ))
- ->submit()
- ->large() !!}
- @else
- {!! Button::success(strtoupper(trans('texts.add_paypal_account') ))
- ->submit()
- ->large() !!}
- @endif
-
- @elseif($paymentType == PAYMENT_TYPE_WEPAY_ACH)
-
{{ trans('texts.bank_account') }}
- @if (!empty($details))
-
{{$details->bank_account_name}}
-
•••••{{$details->bank_account_last_four}}
- @endif
- {!! Former::checkbox('authorize_ach')
- ->text(trans('texts.ach_authorization', ['company'=>$account->getDisplayName(), 'email' => $account->work_email]))
- ->label(' ') !!}
- {!! Former::checkbox('tos_agree')
- ->text(trans('texts.wepay_payment_tos_agree', [
- 'terms' => '
'.trans('texts.terms_of_service').'',
- 'privacy_policy' => '
'.trans('texts.privacy_policy').'',
- ]))
- ->help(trans('texts.payment_processed_through_wepay'))
- ->label(' ') !!}
-
-
-
- @if(isset($amount) && empty($paymentMethodPending))
- {!! Button::success(strtoupper(trans('texts.pay_now') . ' - ' . $account->formatMoney($amount, $client, true) ))
- ->submit()
- ->large() !!}
- @else
- {!! Button::success(strtoupper(trans('texts.add_bank_account') ))
- ->submit()
- ->large() !!}
- @endif
-
- @else
-
-
- @if (!empty($braintreeClientToken))
-
- @else
- {!! Former::text(!empty($tokenize) ? '' : 'card_number')
- ->id('card_number')
- ->placeholder(trans('texts.card_number'))
- ->autocomplete('cc-number')
- ->label('') !!}
- @endif
-
-
- @if (!empty($braintreeClientToken))
-
- @else
- {!! Former::text(!empty($tokenize) ? '' : 'cvv')
- ->id('cvv')
- ->placeholder(trans('texts.cvv'))
- ->autocomplete('off')
- ->label('') !!}
- @endif
-
-
-
-
- @if (!empty($braintreeClientToken))
-
- @else
- {!! Former::select(!empty($tokenize) ? '' : 'expiration_month')
- ->id('expiration_month')
- ->autocomplete('cc-exp-month')
- ->placeholder(trans('texts.expiration_month'))
- ->addOption('01 - January', '1')
- ->addOption('02 - February', '2')
- ->addOption('03 - March', '3')
- ->addOption('04 - April', '4')
- ->addOption('05 - May', '5')
- ->addOption('06 - June', '6')
- ->addOption('07 - July', '7')
- ->addOption('08 - August', '8')
- ->addOption('09 - September', '9')
- ->addOption('10 - October', '10')
- ->addOption('11 - November', '11')
- ->addOption('12 - December', '12')->label('')
- !!}
- @endif
-
-
- @if (!empty($braintreeClientToken))
-
- @else
- {!! Former::select(!empty($tokenize) ? '' : 'expiration_year')
- ->id('expiration_year')
- ->autocomplete('cc-exp-year')
- ->placeholder(trans('texts.expiration_year'))
- ->addOption('2016', '2016')
- ->addOption('2017', '2017')
- ->addOption('2018', '2018')
- ->addOption('2019', '2019')
- ->addOption('2020', '2020')
- ->addOption('2021', '2021')
- ->addOption('2022', '2022')
- ->addOption('2023', '2023')
- ->addOption('2024', '2024')
- ->addOption('2025', '2025')
- ->addOption('2026', '2026')->label('')
- !!}
- @endif
-
-
-
-
- @if (isset($amount) && $client && $account->showTokenCheckbox($storageGateway/* will contain gateway id */))
- selectTokenCheckbox() ? 'CHECKED' : '' }} value="1" style="margin-left:0px; vertical-align:top">
-
-
- @if ($storageGateway == GATEWAY_STRIPE)
- {!! trans('texts.token_billing_secure', ['link' => link_to('https://stripe.com/', 'Stripe.com', ['target' => '_blank'])]) !!}
- @elseif ($storageGateway == GATEWAY_BRAINTREE)
- {!! trans('texts.token_billing_secure', ['link' => link_to('https://www.braintreepayments.com/', 'Braintree', ['target' => '_blank'])]) !!}
- @endif
-
- @endif
-
-
-
- @if (isset($acceptedCreditCardTypes))
-
- @foreach ($acceptedCreditCardTypes as $card)
-
![{{ $card['alt'] }}]({{ $card['source'] }})
- @endforeach
-
- @endif
-
-
-
-
-
- @if(isset($amount))
- {!! Button::success(strtoupper(trans('texts.pay_now') . ' - ' . $account->formatMoney($amount, $client, true) ))
- ->submit()
- ->large() !!}
- @else
- {!! Button::success(strtoupper(trans('texts.add_credit_card') ))
- ->submit()
- ->large() !!}
- @endif
-
-
- @endif
-
-
-
-
-
-
-
-
-
-
-
-
-
- {!! Former::close() !!}
-
-
- @if (isset($accountGateway) && $accountGateway->getPlaidEnabled())
-
{{ trans('texts.secured_by_plaid') }}
-
- @endif
-@stop
diff --git a/resources/views/payments/bank_transfer.blade.php b/resources/views/payments/bank_transfer.blade.php
new file mode 100644
index 000000000000..73a26e010876
--- /dev/null
+++ b/resources/views/payments/bank_transfer.blade.php
@@ -0,0 +1,11 @@
+@extends('payments.payment_method')
+
+@section('head')
+ @parent
+
+ @if (isset($accountGateway) && $accountGateway->getPlaidEnabled())
+
{{ trans('texts.secured_by_plaid') }}
+
+ @endif
+
+@stop
diff --git a/resources/views/payments/braintree/credit_card.blade.php b/resources/views/payments/braintree/credit_card.blade.php
new file mode 100644
index 000000000000..9373b72fb1a2
--- /dev/null
+++ b/resources/views/payments/braintree/credit_card.blade.php
@@ -0,0 +1,73 @@
+@extends('payments.credit_card')
+
+@section('head')
+ @parent
+
+
+
+@stop
diff --git a/resources/views/payments/braintree/paypal.blade.php b/resources/views/payments/braintree/paypal.blade.php
new file mode 100644
index 000000000000..711124f6ecf9
--- /dev/null
+++ b/resources/views/payments/braintree/paypal.blade.php
@@ -0,0 +1,42 @@
+@extends('payments.payment_method')
+
+@section('payment_details')
+ @parent
+
+ {!! Former::open($url) !!}
+
+ {{ trans('texts.paypal') }}
+
+ {{$details->firstName}} {{$details->lastName}}
+ {{$details->email}}
+
+
+
+
+
+
+
+
+ @if (isset($amount) && $client && $account->showTokenCheckbox())
+ selectTokenCheckbox() ? 'CHECKED' : '' }} value="1" style="margin-left:0px; vertical-align:top">
+
+
+ {!! trans('texts.token_billing_secure', ['link' => link_to('https://www.braintreepayments.com/', 'Braintree', ['target' => '_blank'])]) !!}
+
+ @endif
+
+
+
+
+ @if(isset($amount))
+ {!! Button::success(strtoupper(trans('texts.pay_now') . ' - ' . $account->formatMoney($amount, $client, true) ))
+ ->submit()
+ ->large() !!}
+ @else
+ {!! Button::success(strtoupper(trans('texts.add_paypal_account') ))
+ ->submit()
+ ->large() !!}
+ @endif
+
+
+@stop
diff --git a/resources/views/partials/checkout_com_payment.blade.php b/resources/views/payments/checkoutcom/partial.blade.php
similarity index 62%
rename from resources/views/partials/checkout_com_payment.blade.php
rename to resources/views/payments/checkoutcom/partial.blade.php
index b214e2a79209..dc326c2ed9f5 100644
--- a/resources/views/partials/checkout_com_payment.blade.php
+++ b/resources/views/payments/checkoutcom/partial.blade.php
@@ -1,4 +1,4 @@
-@if ($checkoutComDebug)
+@if ($accountGateway->getConfigField('testMode'))
@else
@@ -7,9 +7,9 @@
\ No newline at end of file
+
diff --git a/resources/views/payments/credit_card.blade.php b/resources/views/payments/credit_card.blade.php
new file mode 100644
index 000000000000..6e8b3bff2dc2
--- /dev/null
+++ b/resources/views/payments/credit_card.blade.php
@@ -0,0 +1,259 @@
+@extends('payments.payment_method')
+
+@section('payment_details')
+
+ {!! Former::vertical_open($url)
+ ->autocomplete('on')
+ ->addClass('payment-form')
+ ->id('payment-form')
+ ->rules(array(
+ 'first_name' => 'required',
+ 'last_name' => 'required',
+ 'card_number' => 'required',
+ 'expiration_month' => 'required',
+ 'expiration_year' => 'required',
+ 'cvv' => 'required',
+ 'address1' => 'required',
+ 'city' => 'required',
+ 'state' => 'required',
+ 'postal_code' => 'required',
+ 'country_id' => 'required',
+ 'phone' => 'required',
+ 'email' => 'required|email',
+ 'authorize_ach' => 'required',
+ 'tos_agree' => 'required',
+ 'account_number' => 'required',
+ 'routing_number' => 'required',
+ 'account_holder_name' => 'required',
+ 'account_holder_type' => 'required',
+ )) !!}
+
+
+ @if ($client)
+ {{ Former::populate($client) }}
+ {{ Former::populateField('first_name', $contact->first_name) }}
+ {{ Former::populateField('last_name', $contact->last_name) }}
+ {{ Former::populateField('email', $contact->email) }}
+ @if (!$client->country_id && $client->account->country_id)
+ {{ Former::populateField('country_id', $client->account->country_id) }}
+ @endif
+ @if (!$client->currency_id && $client->account->currency_id)
+ {{ Former::populateField('currency_id', $client->account->currency_id) }}
+ {{ Former::populateField('currency', $client->account->currency->code) }}
+ @endif
+ @endif
+
+ @if (Utils::isNinjaDev())
+ {{ Former::populateField('first_name', 'Test') }}
+ {{ Former::populateField('last_name', 'Test') }}
+ {{ Former::populateField('address1', '350 5th Ave') }}
+ {{ Former::populateField('city', 'New York') }}
+ {{ Former::populateField('state', 'NY') }}
+ {{ Former::populateField('postal_code', '10118') }}
+ {{ Former::populateField('country_id', 840) }}
+
+
+ @endif
+
+ {{ trans('texts.contact_information') }}
+
+
+ {!! Former::text('first_name')
+ ->placeholder(trans('texts.first_name'))
+ ->label('') !!}
+
+
+ {!! Former::text('last_name')
+ ->placeholder(trans('texts.last_name'))
+ ->autocomplete('family-name')
+ ->label('') !!}
+
+
+
+ @if (isset($paymentTitle) || ! empty($contact->email))
+
+
+ {!! Former::text('email')
+ ->placeholder(trans('texts.email'))
+ ->autocomplete('email')
+ ->label('') !!}
+
+
+ @endif
+
+
+
+ @if (!empty($showAddress))
+ {{ trans('texts.billing_address') }} {{ trans('texts.payment_footer1') }}
+
+
+ {!! Former::text('address1')
+ ->autocomplete('address-line1')
+ ->placeholder(trans('texts.address1'))
+ ->label('') !!}
+
+
+ {!! Former::text('address2')
+ ->autocomplete('address-line2')
+ ->placeholder(trans('texts.address2'))
+ ->label('') !!}
+
+
+
+
+ {!! Former::text('city')
+ ->autocomplete('address-level2')
+ ->placeholder(trans('texts.city'))
+ ->label('') !!}
+
+
+ {!! Former::text('state')
+ ->autocomplete('address-level1')
+ ->placeholder(trans('texts.state'))
+ ->label('') !!}
+
+
+
+
+ {!! Former::text('postal_code')
+ ->autocomplete('postal-code')
+ ->placeholder(trans('texts.postal_code'))
+ ->label('') !!}
+
+
+ {!! Former::select('country_id')
+ ->placeholder(trans('texts.country_id'))
+ ->fromQuery(Cache::get('countries'), 'name', 'id')
+ ->addGroupClass('country-select')
+ ->label('') !!}
+
+
+
+
+ @endif
+
+ {{ trans('texts.billing_method') }}
+
+
+
+ @if ($accountGateway->gateway_id == GATEWAY_BRAINTREE)
+
+ @else
+ {!! Former::text(!empty($tokenize) ? '' : 'card_number')
+ ->id('card_number')
+ ->placeholder(trans('texts.card_number'))
+ ->autocomplete('cc-number')
+ ->label('') !!}
+ @endif
+
+
+ @if ($accountGateway->gateway_id == GATEWAY_BRAINTREE)
+
+ @else
+ {!! Former::text(!empty($tokenize) ? '' : 'cvv')
+ ->id('cvv')
+ ->placeholder(trans('texts.cvv'))
+ ->autocomplete('off')
+ ->label('') !!}
+ @endif
+
+
+
+
+ @if ($accountGateway->gateway_id == GATEWAY_BRAINTREE)
+
+ @else
+ {!! Former::select(!empty($tokenize) ? '' : 'expiration_month')
+ ->id('expiration_month')
+ ->autocomplete('cc-exp-month')
+ ->placeholder(trans('texts.expiration_month'))
+ ->addOption('01 - January', '1')
+ ->addOption('02 - February', '2')
+ ->addOption('03 - March', '3')
+ ->addOption('04 - April', '4')
+ ->addOption('05 - May', '5')
+ ->addOption('06 - June', '6')
+ ->addOption('07 - July', '7')
+ ->addOption('08 - August', '8')
+ ->addOption('09 - September', '9')
+ ->addOption('10 - October', '10')
+ ->addOption('11 - November', '11')
+ ->addOption('12 - December', '12')->label('')
+ !!}
+ @endif
+
+
+ @if ($accountGateway->gateway_id == GATEWAY_BRAINTREE)
+
+ @else
+ {!! Former::select(!empty($tokenize) ? '' : 'expiration_year')
+ ->id('expiration_year')
+ ->autocomplete('cc-exp-year')
+ ->placeholder(trans('texts.expiration_year'))
+ ->addOption('2016', '2016')
+ ->addOption('2017', '2017')
+ ->addOption('2018', '2018')
+ ->addOption('2019', '2019')
+ ->addOption('2020', '2020')
+ ->addOption('2021', '2021')
+ ->addOption('2022', '2022')
+ ->addOption('2023', '2023')
+ ->addOption('2024', '2024')
+ ->addOption('2025', '2025')
+ ->addOption('2026', '2026')->label('')
+ !!}
+ @endif
+
+
+
+
+ @if (isset($amount) && $client && $account->showTokenCheckbox($storageGateway/* will contain gateway id */))
+ selectTokenCheckbox() ? 'CHECKED' : '' }} value="1" style="margin-left:0px; vertical-align:top">
+
+
+ @if ($storageGateway == GATEWAY_STRIPE)
+ {!! trans('texts.token_billing_secure', ['link' => link_to('https://stripe.com/', 'Stripe.com', ['target' => '_blank'])]) !!}
+ @elseif ($storageGateway == GATEWAY_BRAINTREE)
+ {!! trans('texts.token_billing_secure', ['link' => link_to('https://www.braintreepayments.com/', 'Braintree', ['target' => '_blank'])]) !!}
+ @endif
+
+ @endif
+
+
+
+ @if (isset($acceptedCreditCardTypes))
+
+ @foreach ($acceptedCreditCardTypes as $card)
+
![{{ $card['alt'] }}]({{ $card['source'] }})
+ @endforeach
+
+ @endif
+
+
+
+
+
+
+
+ @if(isset($amount))
+ {!! Button::success(strtoupper(trans('texts.pay_now') . ' - ' . $account->formatMoney($amount, $client, true) ))
+ ->submit()
+ ->large() !!}
+ @else
+ {!! Button::success(strtoupper(trans('texts.add_credit_card') ))
+ ->submit()
+ ->large() !!}
+ @endif
+
+
+
+@stop
diff --git a/resources/views/payments/payment_css.blade.php b/resources/views/payments/payment_css.blade.php
index 58b39461b462..eda0d427c66b 100644
--- a/resources/views/payments/payment_css.blade.php
+++ b/resources/views/payments/payment_css.blade.php
@@ -15,14 +15,14 @@ body {
.container select,
.braintree-hosted {
@if(!empty($account))
- {!! $account->getBodyFontCss() !!}
+ {!! $account->getBodyFontCss() !!}
@else
- font-weight: 300;
- font-family: 'Roboto', sans-serif;
+ font-weight: 300;
+ font-family: 'Roboto', sans-serif;
@endif
width: 100%;
padding: 11px;
- color: #8c8c8c;
+ color: #444444;
background: #f9f9f9;
border: 1px solid #ebe7e7;
border-radius: 3px;
@@ -66,7 +66,7 @@ div.row {
header {
margin: 0px !important
}
-
+
@media screen and (min-width: 700px) {
header {
margin: 20px 0 75px;
@@ -100,14 +100,14 @@ h3 .help {
}
header h3 {
- text-transform: uppercase;
+ text-transform: uppercase;
}
-
+
header h3 span {
display: inline-block;
margin-left: 8px;
}
-
+
header h3 em {
font-style: normal;
color: #eb8039;
@@ -119,14 +119,14 @@ header h3 em {
background: url({{ asset('/images/icon-shield.png') }}) right 22px no-repeat;
padding: 17px 55px 10px 0;
}
-
+
.secure h3 {
color: #36b855;
font-size: 30px;
margin-bottom: 8px;
margin-top: 0px;
}
-
+
.secure div {
color: #acacac;
font-size: 15px;
diff --git a/resources/views/payments/payment_method.blade.php b/resources/views/payments/payment_method.blade.php
new file mode 100644
index 000000000000..27d58d9b6aba
--- /dev/null
+++ b/resources/views/payments/payment_method.blade.php
@@ -0,0 +1,74 @@
+@extends('public.header')
+
+@section('content')
+
+ @include('payments.payment_css')
+
+
+
+
+
+
+
+
+
+
+ @if ($client && isset($invoiceNumber))
+ {{ $client->getDisplayName() }}
+ {{ trans('texts.invoice') . ' ' . $invoiceNumber }}| {{ trans('texts.amount_due') }}: {{ $account->formatMoney($amount, $client, true) }}
+ @elseif ($paymentTitle)
+ {{ $paymentTitle }}
+ @if(isset($paymentSubtitle))
+
{{ $paymentSubtitle }}
+ @endif
+
+ @endif
+
+
+
+ @if (Request::secure() || Utils::isNinjaDev())
+
+
{{ trans('texts.secure_payment') }}
+
{{ trans('texts.256_encryption') }}
+
+ @endif
+
+
+
+
+
+
+
+ @yield('payment_details')
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {!! Former::close() !!}
+
+
+
+
+@stop
diff --git a/resources/views/payments/paymentmethods_list.blade.php b/resources/views/payments/paymentmethods_list.blade.php
index 153a9847bffa..5208bcd64a15 100644
--- a/resources/views/payments/paymentmethods_list.blade.php
+++ b/resources/views/payments/paymentmethods_list.blade.php
@@ -16,6 +16,7 @@
display:inline-block;
}
+
@if (!empty($braintreeClientToken))
@endif
-@if(!empty($paymentMethods))
-@foreach ($paymentMethods as $paymentMethod)
-
-
-
payment_type->name)))}}">
-
- @if(!empty($paymentMethod->last4))
-
•••••{{$paymentMethod->last4}}
- @endif
- @if($paymentMethod->payment_type_id == PAYMENT_TYPE_ACH)
- @if($paymentMethod->bank_name)
- {{ $paymentMethod->bank_name }}
- @endif
- @if($paymentMethod->status == PAYMENT_METHOD_STATUS_NEW)
- @if($gateway->gateway_id == GATEWAY_STRIPE)
-
({{trans('texts.complete_verification')}})
+
+@if(!empty($paymentMethods) && count($paymentMethods))
+
{{ trans('texts.payment_methods') }}
+
+ @foreach ($paymentMethods as $paymentMethod)
+
+
+
payment_type->name))) }}">
+
+
+ @if($paymentMethod->payment_type_id == PAYMENT_TYPE_ACH)
+ @if($paymentMethod->bank_name)
+ {{ $paymentMethod->bank_name }}
@else
- ({{ trans('texts.verification_pending') }})
+ {{ trans('texts.bank_account') }}
@endif
- @elseif($paymentMethod->status == PAYMENT_METHOD_STATUS_VERIFICATION_FAILED)
- ({{trans('texts.verification_failed')}})
+ @if($paymentMethod->status == PAYMENT_METHOD_STATUS_NEW)
+ @if($gateway->gateway_id == GATEWAY_STRIPE)
+
({{trans('texts.complete_verification')}})
+ @else
+ ({{ trans('texts.verification_pending') }})
+ @endif
+ @elseif($paymentMethod->status == PAYMENT_METHOD_STATUS_VERIFICATION_FAILED)
+ ({{trans('texts.verification_failed')}})
+ @endif
+ @elseif($paymentMethod->payment_type_id == PAYMENT_TYPE_PAYPAL)
+ {{ trans('texts.paypal') . ': ' . $paymentMethod->email }}
+ @else
+ {{ trans('texts.credit_card') }}
@endif
- @elseif($paymentMethod->payment_type_id == PAYMENT_TYPE_ID_PAYPAL)
- {{ $paymentMethod->email }}
- @elseif($paymentMethod->expiration)
- {!! trans('texts.card_expiration', array('expires'=>Utils::fromSqlDate($paymentMethod->expiration, false)->format('m/y'))) !!}
- @endif
- @if($paymentMethod->id == $paymentMethod->account_gateway_token->default_payment_method_id)
- ({{trans('texts.used_for_auto_bill')}})
- @elseif($paymentMethod->payment_type_id != PAYMENT_TYPE_ACH || $paymentMethod->status == PAYMENT_METHOD_STATUS_VERIFIED)
-
({{trans('texts.use_for_auto_bill')}})
- @endif
-
×
-
-@endforeach
+
+ @if($paymentMethod->id == $paymentMethod->account_gateway_token->default_payment_method_id)
+ ({{trans('texts.used_for_auto_bill')}})
+ @elseif($paymentMethod->payment_type_id != PAYMENT_TYPE_ACH || $paymentMethod->status == PAYMENT_METHOD_STATUS_VERIFIED)
+
({{trans('texts.use_for_auto_bill')}})
+ @endif
+
×
+
+
+ @endforeach
@endif
-@if($gateway->gateway_id != GATEWAY_STRIPE || $gateway->getPublishableStripeKey())
+
- {!! Button::success(strtoupper(trans('texts.add_credit_card')))
- ->asLinkTo(URL::to('/client/paymentmethods/add/'.($gateway->getPaymentType() == PAYMENT_TYPE_STRIPE ? 'stripe_credit_card' : 'credit_card'))) !!}
- @if($gateway->getACHEnabled())
-
- @if($gateway->gateway_id == GATEWAY_STRIPE)
- {!! Button::success(strtoupper(trans('texts.add_bank_account')))
- ->asLinkTo(URL::to('/client/paymentmethods/add/stripe_ach')) !!}
- @elseif($gateway->gateway_id == GATEWAY_WEPAY)
- {!! Button::success(strtoupper(trans('texts.add_bank_account')))
- ->withAttributes(['id'=>'add-ach'])
- ->asLinkTo(URL::to('/client/paymentmethods/add/wepay_ach')) !!}
- @endif
- @endif
- @if($gateway->getPayPalEnabled())
+ @if (false && $account->getGatewayByType(GATEWAY_TYPE_CREDIT_CARD) && $account->getGatewayByType(GATEWAY_TYPE_TOKEN))
+ {!! Button::success(strtoupper(trans('texts.add_credit_card')))
+ ->asLinkTo(URL::to('/client/add/credit_card')) !!}
+ @endif
+ @if (false && $account->getGatewayByType(GATEWAY_TYPE_BANK_TRANSFER) && $account->getGatewayByType(GATEWAY_TYPE_TOKEN))
+ {!! Button::success(strtoupper(trans('texts.add_bank_account')))
+ ->withAttributes(['id'=>'add-ach'])
+ ->asLinkTo(URL::to('/client/add/bank_transfer')) !!}
+
+ @endif
+ @if (false && $account->getGatewayByType(GATEWAY_TYPE_PAYPAL) && $account->getGatewayByType(GATEWAY_TYPE_TOKEN))
{!! Button::success(strtoupper(trans('texts.add_paypal_account')))
->withAttributes(['id'=>'add-paypal'])
- ->asLinkTo(URL::to('/client/paymentmethods/add/braintree_paypal')) !!}
+ ->asLinkTo(URL::to('/client/add/paypal')) !!}
@endif
-@endif
- {!! Former::open('/client/paymentmethods/verify') !!}
+ {!! Former::open('/client/payment_methods/verify') !!}
+
+ @if (Utils::isNinjaDev())
+
+ @endif
+
-{!! Former::open(URL::to('/client/paymentmethods/default'))->id('defaultSourceForm') !!}
-
+
+{!! Former::open(URL::to('/client/payment_methods/default'))->id('defaultSourceForm') !!}
+
{!! Former::close() !!}
+
+
+@stop
+
+
+@section('payment_details')
+
+ @if ($accountGateway->getPlaidEnabled())
+
+ @endif
+
+
+ @if($accountGateway->getPlaidEnabled())
+
{{ trans('texts.or') }}
+
{{ trans('texts.link_manually') }}
+ @endif
+
+
{{ trans('texts.ach_verification_delay_help') }}
+
+ {!! Former::open($url)
+ ->autocomplete('on')
+ ->addClass('payment-form')
+ ->id('payment-form')
+ ->rules(array(
+ 'country_id' => 'required',
+ 'currency_id' => 'required',
+ 'account_number' => 'required',
+ 'routing_number' => 'required',
+ 'account_holder_name' => 'required',
+ 'account_holder_type' => 'required',
+ 'authorize_ach' => 'required',
+ )) !!}
+
+ {!! Former::populateField('account_holder_type', 'individual') !!}
+ {!! Former::populateField('country_id', $client->country_id) !!}
+ {!! Former::populateField('currency_id', $client->getCurrencyCode()) !!}
+
+ @if (Utils::isNinjaDev())
+ {!! Former::populateField('account_holder_name', 'Test Client') !!}
+
+ @endif
+
+ {!! Former::radios('account_holder_type')->radios(array(
+ trans('texts.individual_account') => array('value' => 'individual'),
+ trans('texts.company_account') => array('value' => 'company'),
+ ))->inline()->label(trans('texts.account_holder_type')); !!}
+
+ {!! Former::text('account_holder_name')
+ ->label(trans('texts.account_holder_name')) !!}
+
+ {!! Former::select('country_id')
+ ->label(trans('texts.country_id'))
+ ->addOption('','')
+ ->fromQuery(Cache::get('countries'), 'name', 'id')
+ ->addGroupClass('country-select') !!}
+
+ {!! Former::select('currency_id')
+ ->label(trans('texts.currency_id'))
+ ->addOption('','')
+ ->fromQuery(Cache::get('currencies'), 'name', 'code')
+ ->addGroupClass('currency-select') !!}
+
+ {!! Former::text('')
+ ->id('routing_number')
+ ->label(trans('texts.routing_number')) !!}
+
+
+
+ {!! Former::text('')
+ ->id('account_number')
+ ->label(trans('texts.account_number')) !!}
+ {!! Former::text('')
+ ->id('confirm_account_number')
+ ->label(trans('texts.confirm_account_number')) !!}
+
+
+ {!! Former::checkbox('authorize_ach')
+ ->text(trans('texts.ach_authorization', ['company'=>$account->getDisplayName(), 'email' => $account->work_email]))
+ ->label(' ') !!}
+
+
+
+
+
+
+
+
+ {!! Button::success(strtoupper(trans('texts.add_account')))
+ ->submit()
+ ->withAttributes(['id'=>'add_account_button'])
+ ->large() !!}
+
+ @if($accountGateway->getPlaidEnabled() && !empty($amount))
+ {!! Button::success(strtoupper(trans('texts.pay_now') . ' - ' . $account->formatMoney($amount, $client, true) ))
+ ->submit()
+ ->withAttributes(['style'=>'display:none', 'id'=>'pay_now_button'])
+ ->large() !!}
+ @endif
+
+
+
+
+
+@stop
diff --git a/resources/views/payments/stripe/credit_card.blade.php b/resources/views/payments/stripe/credit_card.blade.php
new file mode 100644
index 000000000000..d2819bfd30ea
--- /dev/null
+++ b/resources/views/payments/stripe/credit_card.blade.php
@@ -0,0 +1,89 @@
+@extends('payments.credit_card')
+
+@section('head')
+ @parent
+
+ @if ($accountGateway->getPublishableStripeKey())
+
+
+ @else
+
+ @endif
+@stop
diff --git a/resources/views/payments/tokenization_braintree.blade.php b/resources/views/payments/tokenization_braintree.blade.php
deleted file mode 100644
index 3588ff9a67a0..000000000000
--- a/resources/views/payments/tokenization_braintree.blade.php
+++ /dev/null
@@ -1,67 +0,0 @@
-
-
\ No newline at end of file
diff --git a/resources/views/payments/tokenization_stripe.blade.php b/resources/views/payments/tokenization_stripe.blade.php
deleted file mode 100644
index 325393061866..000000000000
--- a/resources/views/payments/tokenization_stripe.blade.php
+++ /dev/null
@@ -1,154 +0,0 @@
-
-
\ No newline at end of file
diff --git a/resources/views/payments/wepay/bank_transfer.blade.php b/resources/views/payments/wepay/bank_transfer.blade.php
new file mode 100644
index 000000000000..e273d002673b
--- /dev/null
+++ b/resources/views/payments/wepay/bank_transfer.blade.php
@@ -0,0 +1,40 @@
+@extends('payments.bank_transfer')
+
+@section('payment_details')
+
+
{{ trans('texts.bank_account') }}
+
+ @if (!empty($details))
+
{{$details->bank_account_name}}
+
•••••{{$details->bank_account_last_four}}
+ @endif
+
+ {!! Former::checkbox('authorize_ach')
+ ->text(trans('texts.ach_authorization', ['company'=>$account->getDisplayName(), 'email' => $account->work_email]))
+ ->label(' ') !!}
+
+ {!! Former::checkbox('tos_agree')
+ ->text(trans('texts.wepay_payment_tos_agree', [
+ 'terms' => '
'.trans('texts.terms_of_service').'',
+ 'privacy_policy' => '
'.trans('texts.privacy_policy').'',
+ ]))
+ ->help(trans('texts.payment_processed_through_wepay'))
+ ->label(' ') !!}
+
+
+
+
+
+
+ @if(isset($amount) && empty($paymentMethodPending))
+ {!! Button::success(strtoupper(trans('texts.pay_now') . ' - ' . $account->formatMoney($amount, $client, true) ))
+ ->submit()
+ ->large() !!}
+ @else
+ {!! Button::success(strtoupper(trans('texts.add_bank_account') ))
+ ->submit()
+ ->large() !!}
+ @endif
+
+
+@stop
diff --git a/resources/views/public/header.blade.php b/resources/views/public/header.blade.php
index 7ccc64f603f8..257196c59884 100644
--- a/resources/views/public/header.blade.php
+++ b/resources/views/public/header.blade.php
@@ -69,7 +69,7 @@
@if (!isset($account) || !$account->hasFeature(FEATURE_WHITE_LABEL))
{{-- Per our license, please do not remove or modify this link. --}}
 }})
- @endif
+ @endif
@if (!isset($account) || $account->isNinjaAccount() || $account->enable_client_portal)
@@ -91,13 +91,13 @@
@endif
@if (isset($account) && $account->getTokenGatewayId() && !$account->enable_client_portal_dashboard)
-
- {!! link_to('/client/paymentmethods', trans('texts.payment_methods') ) !!}
+
+ {!! link_to('/client/payment_methods', trans('texts.payment_methods') ) !!}
@endif
{!! link_to('/client/payments', trans('texts.payments') ) !!}
-
+
@endif
@@ -137,16 +137,16 @@
+
-
- @endif
+
+ @endif
-
+
@if (!isset($account) || !$account->hasFeature(FEATURE_WHITE_LABEL))