mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-07 18:54:30 -04:00
Payments refactor
This commit is contained in:
parent
e2e629141b
commit
5ff69c6f29
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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 = '<div>' . htmlentities($paymentMethod->bank_name) . '</div>';
|
||||
} else {
|
||||
$html = '<img height="22" src="'.URL::to('/images/credit_cards/ach.png').'" style="float:left" alt="'.trans("texts.direct_debit").'">';
|
||||
}
|
||||
} elseif ($paymentMethod->payment_type_id == PAYMENT_TYPE_ID_PAYPAL) {
|
||||
$html = '<img height="22" src="'.URL::to('/images/credit_cards/paypal.png').'" alt="'.trans("texts.card_".$code).'">';
|
||||
} else {
|
||||
$html = '<img height="22" src="'.URL::to('/images/credit_cards/'.$code.'.png').'" alt="'.trans("texts.card_".$code).'">';
|
||||
}
|
||||
|
||||
$url = URL::to("/payment/{$invitation->invitation_key}/token/".$paymentMethod->public_id);
|
||||
|
||||
if ($paymentMethod->payment_type_id == PAYMENT_TYPE_ID_PAYPAL) {
|
||||
$html .= ' <span>'.$paymentMethod->email.'</span>';
|
||||
$url .= '#braintree_paypal';
|
||||
} elseif ($paymentMethod->payment_type_id != PAYMENT_TYPE_ACH) {
|
||||
$html .= '<div class="pull-right" style="text-align:right">'.trans('texts.card_expiration', array('expires' => Utils::fromSqlDate($paymentMethod->expiration, false)->format('m/y'))).'<br>';
|
||||
$html .= '•••'.$paymentMethod->last4.'</div>';
|
||||
} else {
|
||||
$html .= '<div style="text-align:right">';
|
||||
$html .= '•••'.$paymentMethod->last4.'</div>';
|
||||
}
|
||||
|
||||
$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)
|
||||
|
@ -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) {
|
||||
|
236
app/Http/Controllers/NinjaController.php
Normal file
236
app/Http/Controllers/NinjaController.php
Normal file
@ -0,0 +1,236 @@
|
||||
<?php namespace App\Http\Controllers;
|
||||
|
||||
use CreditCard;
|
||||
use Session;
|
||||
use Input;
|
||||
use Utils;
|
||||
use View;
|
||||
use Validator;
|
||||
use URL;
|
||||
use Cache;
|
||||
use Omnipay;
|
||||
use App\Models\Country;
|
||||
use App\Models\License;
|
||||
use App\Models\Affiliate;
|
||||
use App\Ninja\Repositories\AccountRepository;
|
||||
use App\Ninja\Mailers\ContactMailer;
|
||||
|
||||
class NinjaController extends BaseController
|
||||
{
|
||||
public function __construct(AccountRepository $accountRepo, ContactMailer $contactMailer)
|
||||
{
|
||||
$this->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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
333
app/Http/Controllers/OnlinePaymentController.php
Normal file
333
app/Http/Controllers/OnlinePaymentController.php
Normal file
@ -0,0 +1,333 @@
|
||||
<?php namespace App\Http\Controllers;
|
||||
|
||||
use Session;
|
||||
use Input;
|
||||
use Request;
|
||||
use Utils;
|
||||
use View;
|
||||
use Validator;
|
||||
use Cache;
|
||||
use Exception;
|
||||
use App\Models\Invitation;
|
||||
use App\Models\Account;
|
||||
use App\Models\Payment;
|
||||
use App\Models\PaymentMethod;
|
||||
use App\Services\PaymentService;
|
||||
use App\Ninja\Mailers\UserMailer;
|
||||
use App\Http\Requests\CreateOnlinePaymentRequest;
|
||||
|
||||
class OnlinePaymentController extends BaseController
|
||||
{
|
||||
public function __construct(PaymentService $paymentService, UserMailer $userMailer)
|
||||
{
|
||||
$this->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');
|
||||
}
|
||||
|
||||
}
|
@ -1,33 +1,16 @@
|
||||
<?php namespace App\Http\Controllers;
|
||||
|
||||
use Datatable;
|
||||
use Input;
|
||||
use Redirect;
|
||||
use Request;
|
||||
use Session;
|
||||
use Utils;
|
||||
use View;
|
||||
use Validator;
|
||||
use Omnipay;
|
||||
use CreditCard;
|
||||
use URL;
|
||||
use Cache;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Invitation;
|
||||
use App\Models\Client;
|
||||
use App\Models\Account;
|
||||
use App\Models\PaymentType;
|
||||
use App\Models\License;
|
||||
use App\Models\Payment;
|
||||
use App\Models\Affiliate;
|
||||
use App\Models\PaymentMethod;
|
||||
use App\Ninja\Repositories\PaymentRepository;
|
||||
use App\Ninja\Repositories\InvoiceRepository;
|
||||
use App\Ninja\Repositories\AccountRepository;
|
||||
use App\Ninja\Mailers\ContactMailer;
|
||||
use App\Ninja\Mailers\UserMailer;
|
||||
use App\Services\PaymentService;
|
||||
|
||||
use App\Http\Requests\PaymentRequest;
|
||||
use App\Http\Requests\CreatePaymentRequest;
|
||||
use App\Http\Requests\UpdatePaymentRequest;
|
||||
@ -36,16 +19,11 @@ class PaymentController extends BaseController
|
||||
{
|
||||
protected $entityType = ENTITY_PAYMENT;
|
||||
|
||||
public function __construct(PaymentRepository $paymentRepo, InvoiceRepository $invoiceRepo, AccountRepository $accountRepo, ContactMailer $contactMailer, PaymentService $paymentService, UserMailer $userMailer)
|
||||
public function __construct(PaymentRepository $paymentRepo, ContactMailer $contactMailer, PaymentService $paymentService)
|
||||
{
|
||||
// parent::__construct();
|
||||
|
||||
$this->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');
|
||||
}
|
||||
}
|
||||
|
46
app/Http/Requests/CreateOnlinePaymentRequest.php
Normal file
46
app/Http/Requests/CreateOnlinePaymentRequest.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php namespace App\Http\Requests;
|
||||
|
||||
use App\Models\Invitation;
|
||||
|
||||
class CreateOnlinePaymentRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
$account = $this->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();
|
||||
}
|
||||
}
|
@ -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');
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
|
@ -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',''));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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)));
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -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];
|
||||
}
|
||||
|
||||
|
@ -49,12 +49,6 @@ class AccountGatewayDatatable extends EntityDatatable
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
'payment_type',
|
||||
function ($model) {
|
||||
return Gateway::getPrettyPaymentType($model->gateway_id);
|
||||
}
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -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]);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
@ -0,0 +1,6 @@
|
||||
<?php namespace App\Ninja\PaymentDrivers;
|
||||
|
||||
class AuthorizeNetAIMPaymentDriver extends BasePaymentDriver
|
||||
{
|
||||
protected $transactionReferenceParam = 'refId';
|
||||
}
|
796
app/Ninja/PaymentDrivers/BasePaymentDriver.php
Normal file
796
app/Ninja/PaymentDrivers/BasePaymentDriver.php
Normal file
@ -0,0 +1,796 @@
|
||||
<?php namespace App\Ninja\PaymentDrivers;
|
||||
|
||||
use URL;
|
||||
use Session;
|
||||
use Request;
|
||||
use Omnipay;
|
||||
use Exception;
|
||||
use CreditCard;
|
||||
use App\Models\AccountGatewayToken;
|
||||
use App\Models\Payment;
|
||||
use App\Models\PaymentMethod;
|
||||
use App\Models\Country;
|
||||
|
||||
class BasePaymentDriver
|
||||
{
|
||||
public $invitation;
|
||||
public $accountGateway;
|
||||
|
||||
protected $gatewayType;
|
||||
protected $gateway;
|
||||
protected $customer;
|
||||
protected $sourceId;
|
||||
protected $input;
|
||||
|
||||
protected $customerResponse;
|
||||
protected $tokenResponse;
|
||||
protected $purchaseResponse;
|
||||
|
||||
protected $sourceReferenceParam;
|
||||
protected $customerReferenceParam;
|
||||
protected $transactionReferenceParam;
|
||||
|
||||
public function __construct($accountGateway, $invitation = false, $gatewayType = false)
|
||||
{
|
||||
$this->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;
|
||||
}
|
||||
}
|
||||
}
|
12
app/Ninja/PaymentDrivers/BitPayPaymentDriver.php
Normal file
12
app/Ninja/PaymentDrivers/BitPayPaymentDriver.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php namespace App\Ninja\PaymentDrivers;
|
||||
|
||||
class BitPayPaymentDriver extends BasePaymentDriver
|
||||
{
|
||||
protected function gatewayTypes()
|
||||
{
|
||||
return [
|
||||
GATEWAY_TYPE_BITCOIN
|
||||
];
|
||||
}
|
||||
|
||||
}
|
193
app/Ninja/PaymentDrivers/BraintreePaymentDriver.php
Normal file
193
app/Ninja/PaymentDrivers/BraintreePaymentDriver.php
Normal file
@ -0,0 +1,193 @@
|
||||
<?php namespace App\Ninja\PaymentDrivers;
|
||||
|
||||
use Exception;
|
||||
use Session;
|
||||
use Braintree\Customer;
|
||||
|
||||
class BraintreePaymentDriver extends BasePaymentDriver
|
||||
{
|
||||
protected $customerReferenceParam = 'customerId';
|
||||
protected $sourceReferenceParam = 'paymentMethodToken';
|
||||
|
||||
protected function gatewayTypes()
|
||||
{
|
||||
$types = [
|
||||
GATEWAY_TYPE_CREDIT_CARD,
|
||||
GATEWAY_TYPE_TOKEN,
|
||||
];
|
||||
|
||||
if ($this->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();
|
||||
}
|
||||
}
|
35
app/Ninja/PaymentDrivers/CheckoutComPaymentDriver.php
Normal file
35
app/Ninja/PaymentDrivers/CheckoutComPaymentDriver.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php namespace App\Ninja\PaymentDrivers;
|
||||
|
||||
class CheckoutComPaymentDriver extends BasePaymentDriver
|
||||
{
|
||||
public function createTransactionToken()
|
||||
{
|
||||
$response = $this->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;
|
||||
}
|
||||
|
||||
}
|
15
app/Ninja/PaymentDrivers/CybersourcePaymentDriver.php
Normal file
15
app/Ninja/PaymentDrivers/CybersourcePaymentDriver.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php namespace App\Ninja\PaymentDrivers;
|
||||
|
||||
class CybersourcePaymentDriver extends BasePaymentDriver
|
||||
{
|
||||
protected $transactionReferenceParam = 'transaction_uuid';
|
||||
|
||||
public function completeOffsitePurchase($input)
|
||||
{
|
||||
if ($input['decision'] == 'ACCEPT') {
|
||||
return $this->createPayment($input['bill_trans_ref_no']);
|
||||
} else {
|
||||
throw new Exception($input['message'] . ': ' . $input['invalid_fields']);
|
||||
}
|
||||
}
|
||||
}
|
24
app/Ninja/PaymentDrivers/DwollaPaymentDriver.php
Normal file
24
app/Ninja/PaymentDrivers/DwollaPaymentDriver.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php namespace App\Ninja\PaymentDrivers;
|
||||
|
||||
class DwollaPaymentDriver extends BasePaymentDriver
|
||||
{
|
||||
protected function gatewayTypes()
|
||||
{
|
||||
return [GATEWAY_TYPE_DWOLLA];
|
||||
}
|
||||
|
||||
protected function gateway()
|
||||
{
|
||||
$gateway = parent::gateway();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
<?php namespace App\Ninja\PaymentDrivers;
|
||||
|
||||
class EwayRapidSharedPaymentDriver extends BasePaymentDriver
|
||||
{
|
||||
protected $transactionReferenceParam = 'AccessCode';
|
||||
}
|
6
app/Ninja/PaymentDrivers/GoCardlessPaymentDriver.php
Normal file
6
app/Ninja/PaymentDrivers/GoCardlessPaymentDriver.php
Normal file
@ -0,0 +1,6 @@
|
||||
<?php namespace App\Ninja\PaymentDrivers;
|
||||
|
||||
class GoCardlessPaymentDriver extends BasePaymentDriver
|
||||
{
|
||||
protected $transactionReferenceParam = 'signature';
|
||||
}
|
16
app/Ninja/PaymentDrivers/MolliePaymentDriver.php
Normal file
16
app/Ninja/PaymentDrivers/MolliePaymentDriver.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php namespace App\Ninja\PaymentDrivers;
|
||||
|
||||
class MolliePaymentDriver extends BasePaymentDriver
|
||||
{
|
||||
public function completeOffsitePurchase($input)
|
||||
{
|
||||
$details = $this->paymentDetails();
|
||||
|
||||
$details['transactionReference'] = $this->invitation->transaction_reference;
|
||||
|
||||
$response = $this->gateway()->fetchTransaction($details)->send();
|
||||
|
||||
return $this->createPayment($response->getTransactionReference());
|
||||
}
|
||||
|
||||
}
|
13
app/Ninja/PaymentDrivers/PayFastPaymentDriver.php
Normal file
13
app/Ninja/PaymentDrivers/PayFastPaymentDriver.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php namespace App\Ninja\PaymentDrivers;
|
||||
|
||||
class PayFastPaymentDriver extends BasePaymentDriver
|
||||
{
|
||||
protected $transactionReferenceParam = 'm_payment_id';
|
||||
|
||||
public function completeOffsitePurchase($input)
|
||||
{
|
||||
if ($accountGateway->isGateway(GATEWAY_PAYFAST) && Request::has('pt')) {
|
||||
$token = Request::query('pt');
|
||||
}
|
||||
}
|
||||
}
|
29
app/Ninja/PaymentDrivers/PayPalExpressPaymentDriver.php
Normal file
29
app/Ninja/PaymentDrivers/PayPalExpressPaymentDriver.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php namespace App\Ninja\PaymentDrivers;
|
||||
|
||||
use Exception;
|
||||
|
||||
class PayPalExpressPaymentDriver extends BasePaymentDriver
|
||||
{
|
||||
protected function gatewayTypes()
|
||||
{
|
||||
return [
|
||||
GATEWAY_TYPE_PAYPAL
|
||||
];
|
||||
}
|
||||
|
||||
protected function paymentDetails($paymentMethod = false)
|
||||
{
|
||||
$data = parent::paymentDetails();
|
||||
|
||||
$data['ButtonSource'] = 'InvoiceNinja_SP';
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
protected function creatingPayment($payment)
|
||||
{
|
||||
$payment->payer_id = $this->input['PayerID'];
|
||||
|
||||
return $payment;
|
||||
}
|
||||
}
|
20
app/Ninja/PaymentDrivers/PayPalProPaymentDriver.php
Normal file
20
app/Ninja/PaymentDrivers/PayPalProPaymentDriver.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?php namespace App\Ninja\PaymentDrivers;
|
||||
|
||||
class PayPalProPaymentDriver extends BasePaymentDriver
|
||||
{
|
||||
protected function gatewayTypes()
|
||||
{
|
||||
return [
|
||||
GATEWAY_TYPE_CREDIT_CARD
|
||||
];
|
||||
}
|
||||
|
||||
protected function paymentDetails($paymentMethod = false)
|
||||
{
|
||||
$data = parent::paymentDetails();
|
||||
|
||||
$data['ButtonSource'] = 'InvoiceNinja_SP';
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
304
app/Ninja/PaymentDrivers/StripePaymentDriver.php
Normal file
304
app/Ninja/PaymentDrivers/StripePaymentDriver.php
Normal file
@ -0,0 +1,304 @@
|
||||
<?php namespace App\Ninja\PaymentDrivers;
|
||||
|
||||
use Exception;
|
||||
use Cache;
|
||||
use App\Models\PaymentMethod;
|
||||
|
||||
class StripePaymentDriver extends BasePaymentDriver
|
||||
{
|
||||
protected $customerReferenceParam = 'customerReference';
|
||||
|
||||
protected function gatewayTypes()
|
||||
{
|
||||
$types = [
|
||||
GATEWAY_TYPE_CREDIT_CARD,
|
||||
GATEWAY_TYPE_TOKEN
|
||||
];
|
||||
|
||||
if ($this->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();
|
||||
}
|
||||
}
|
||||
}
|
13
app/Ninja/PaymentDrivers/TwoCheckoutPaymentDriver.php
Normal file
13
app/Ninja/PaymentDrivers/TwoCheckoutPaymentDriver.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php namespace App\Ninja\PaymentDrivers;
|
||||
|
||||
class TwoCheckoutPaymentDriver extends BasePaymentDriver
|
||||
{
|
||||
protected $transactionReferenceParam = 'cart_order_id';
|
||||
|
||||
// Calling completePurchase results in an 'invalid key' error
|
||||
public function completeOffsitePurchase($input)
|
||||
{
|
||||
return $this->createPayment($input['order_number']);
|
||||
}
|
||||
|
||||
}
|
118
app/Ninja/PaymentDrivers/WePayPaymentDriver.php
Normal file
118
app/Ninja/PaymentDrivers/WePayPaymentDriver.php
Normal file
@ -0,0 +1,118 @@
|
||||
<?php namespace App\Ninja\PaymentDrivers;
|
||||
|
||||
class WePayPaymentDriver extends BasePaymentDriver
|
||||
{
|
||||
protected function gatewayTypes()
|
||||
{
|
||||
return [
|
||||
GATEWAY_TYPE_CREDIT_CARD,
|
||||
GATEWAY_TYPE_BANK_TRANSFER,
|
||||
GATEWAY_TYPE_TOKEN
|
||||
];
|
||||
}
|
||||
|
||||
public function startPurchase($input, $sourceId)
|
||||
{
|
||||
$data = parent::startPurchase($input, $sourceId);
|
||||
|
||||
if ($this->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.
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
|
@ -114,10 +114,8 @@ class DatatableService
|
||||
. trans("texts.archive_{$datatable->entityType}") . "</a></li>";
|
||||
}
|
||||
} else if($can_edit) {
|
||||
if ($datatable->entityType != ENTITY_ACCOUNT_GATEWAY || Auth::user()->account->canAddGateway(\App\Models\Gateway::getPaymentType($model->gateway_id))) {
|
||||
$dropdown_contents .= "<li><a href=\"javascript:restoreEntity({$model->public_id})\">"
|
||||
. trans("texts.restore_{$datatable->entityType}") . "</a></li>";
|
||||
}
|
||||
$dropdown_contents .= "<li><a href=\"javascript:restoreEntity({$model->public_id})\">"
|
||||
. trans("texts.restore_{$datatable->entityType}") . "</a></li>";
|
||||
}
|
||||
|
||||
if (property_exists($model, 'is_deleted') && !$model->is_deleted && $can_edit) {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ class TemplateService
|
||||
}
|
||||
$documentsHTML .= '</ul>';
|
||||
}
|
||||
|
||||
|
||||
$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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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],
|
||||
|
@ -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([
|
||||
|
@ -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',
|
||||
|
||||
);
|
||||
|
||||
|
@ -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)
|
||||
<div style="display: none">
|
||||
{!! Former::text('primary_gateway_id')->value($accountGateway->gateway_id) !!}
|
||||
{!! Former::text('primary_gateway_id') !!}
|
||||
</div>
|
||||
@else
|
||||
{!! Former::select('primary_gateway_id')
|
||||
@ -168,7 +166,7 @@
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -203,6 +201,12 @@
|
||||
} else {
|
||||
$('.onsite-fields').show();
|
||||
}
|
||||
|
||||
if (gateway.id == {{ GATEWAY_STRIPE }}) {
|
||||
$('.stripe-ach').show();
|
||||
} else {
|
||||
$('.stripe-ach').hide();
|
||||
}
|
||||
}
|
||||
|
||||
function gatewayLink(url) {
|
||||
|
@ -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'=>'<a id="wepay-tos-link" href="https://go.wepay.com/terms-of-service-us" target="_blank">'.trans('texts.wepay_tos_link_text').'</a>']
|
||||
@ -83,7 +74,7 @@
|
||||
|
||||
<br/>
|
||||
<center>
|
||||
{!! 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() !!}
|
||||
</center>
|
||||
|
||||
@ -108,7 +99,7 @@
|
||||
})
|
||||
</script>
|
||||
|
||||
<input type="hidden" name="gateway_id" value="{{ GATEWAY_WEPAY }}">
|
||||
<input type="hidden" name="primary_gateway_id" value="{{ GATEWAY_WEPAY }}">
|
||||
{!! Former::close() !!}
|
||||
|
||||
@stop
|
||||
|
@ -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) !!}
|
@ -31,11 +31,13 @@
|
||||
</div>
|
||||
{!! Former::close() !!}
|
||||
|
||||
<!--
|
||||
<label for="trashed" style="font-weight:normal; margin-left: 10px;">
|
||||
<input id="trashed" type="checkbox" onclick="setTrashVisible()"
|
||||
{{ Session::get("show_trash:gateway") ? 'checked' : ''}}/> {{ trans('texts.show_archived_deleted')}} {{ Utils::transFlowText('gateways') }}
|
||||
</label>
|
||||
|
||||
-->
|
||||
|
||||
@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') !!}
|
||||
|
||||
<script>
|
||||
|
@ -132,10 +132,10 @@
|
||||
<li>$customInvoice1</li>
|
||||
@endif
|
||||
@if (count($account->account_gateways) > 1)
|
||||
@foreach (\App\Models\Gateway::$paymentTypes as $type)
|
||||
@foreach (\App\Models\Gateway::$gatewayTypes as $type)
|
||||
@if ($account->getGatewayByType($type))
|
||||
<li>${{ \App\Models\Gateway::getPaymentTypeName($type) }}Link</li>
|
||||
<li>${{ \App\Models\Gateway::getPaymentTypeName($type) }}Button</li>
|
||||
<li>${{ Utils::toCamelCase($type) }}Link</li>
|
||||
<li>${{ Utils::toCamelCase($type) }}Button</li>
|
||||
@endif
|
||||
@endforeach
|
||||
@endif
|
||||
@ -180,7 +180,7 @@
|
||||
var previewName = '#' + entityType + '_' + stringType + '_preview';
|
||||
$(previewName).html(processVariables(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function serverPreview(field) {
|
||||
@ -189,14 +189,14 @@
|
||||
var template = $('#email_template_' + field).val();
|
||||
var url = '{{ URL::to('settings/email_preview') }}?template=' + template;
|
||||
$('#server-preview').attr('src', url).load(function() {
|
||||
// disable links in the preview
|
||||
// disable links in the preview
|
||||
$('iframe').contents().find('a').each(function(index) {
|
||||
$(this).on('click', function(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
$(function() {
|
||||
@ -219,7 +219,7 @@
|
||||
refreshPreview();
|
||||
});
|
||||
|
||||
function enableReminder(id) {
|
||||
function enableReminder(id) {
|
||||
var checked = $('#enable_reminder' + id).is(':checked');
|
||||
$('.enable-reminder' + id).attr('disabled', !checked)
|
||||
}
|
||||
@ -242,29 +242,29 @@
|
||||
|
||||
var keys = {!! json_encode(\App\Ninja\Mailers\ContactMailer::$variableFields) !!};
|
||||
var passwordHtml = "{!! $account->isPro() && $account->enable_portal_password && $account->send_portal_password?'<p>'.trans('texts.password').': 6h2NWNdw6<p>':'' !!}";
|
||||
|
||||
|
||||
@if ($account->isPro())
|
||||
var documentsHtml = "{!! trans('texts.email_documents_header').'<ul><li><a>'.trans('texts.email_documents_example_1').'</a></li><li><a>'.trans('texts.email_documents_example_2').'</a></li></ul>' !!}";
|
||||
@else
|
||||
var documentsHtml = "";
|
||||
@endif
|
||||
|
||||
|
||||
var vals = [
|
||||
{!! json_encode($emailFooter) !!},
|
||||
"{{ $account->getDisplayName() }}",
|
||||
{!! json_encode($emailFooter) !!},
|
||||
"{{ $account->getDisplayName() }}",
|
||||
"{{ $account->formatDate($account->getDateTime()) }}",
|
||||
"{{ $account->formatDate($account->getDateTime()) }}",
|
||||
"Client Name",
|
||||
formatMoney(100),
|
||||
"Contact Name",
|
||||
"Client Name",
|
||||
formatMoney(100),
|
||||
"Contact Name",
|
||||
"First Name",
|
||||
"0001",
|
||||
"0001",
|
||||
"0001",
|
||||
passwordHtml,
|
||||
documentsHtml,
|
||||
"{{ URL::to('/view/...') }}$password",
|
||||
"{{ URL::to('/view/...') }}$password",
|
||||
'{!! Form::flatButton('view_invoice', '#0b4d78') !!}$password',
|
||||
"{{ URL::to('/payment/...') }}$password",
|
||||
"{{ URL::to('/payment/...') }}$password",
|
||||
'{!! Form::flatButton('pay_now', '#36c157') !!}$password',
|
||||
'{{ trans('texts.auto_bill_notification_placeholder') }}',
|
||||
];
|
||||
@ -274,21 +274,21 @@
|
||||
vals.push('custom value', 'custom value', 'custom value', 'custom value');
|
||||
|
||||
// Add any available payment method links
|
||||
@foreach (\App\Models\Gateway::$paymentTypes as $type)
|
||||
{!! "keys.push('" . \App\Models\Gateway::getPaymentTypeName($type).'Link' . "');" !!}
|
||||
@foreach (\App\Models\Gateway::$gatewayTypes as $type)
|
||||
{!! "keys.push('" . Utils::toCamelCase($type).'Link' . "');" !!}
|
||||
{!! "vals.push('" . URL::to('/payment/...') . "');" !!}
|
||||
|
||||
{!! "keys.push('" . \App\Models\Gateway::getPaymentTypeName($type).'Button' . "');" !!}
|
||||
{!! "keys.push('" . Utils::toCamelCase($type).'Button' . "');" !!}
|
||||
{!! "vals.push('" . Form::flatButton('pay_now', '#36c157') . "');" !!}
|
||||
@endforeach
|
||||
|
||||
var includesPasswordPlaceholder = str.indexOf('$password') != -1;
|
||||
|
||||
|
||||
for (var i=0; i<keys.length; i++) {
|
||||
var regExp = new RegExp('\\$'+keys[i], 'g');
|
||||
str = str.replace(regExp, vals[i]);
|
||||
}
|
||||
|
||||
|
||||
if(!includesPasswordPlaceholder){
|
||||
var lastSpot = str.lastIndexOf('$password')
|
||||
str = str.slice(0, lastSpot) + str.slice(lastSpot).replace('$password', passwordHtml);
|
||||
|
@ -39,7 +39,9 @@
|
||||
</div>
|
||||
|
||||
@if ($gatewayLink)
|
||||
{!! Button::normal(trans('texts.view_in_gateway', ['gateway'=>$gateway->gateway->name]))->asLinkTo($gatewayLink)->withAttributes(['target' => '_blank']) !!}
|
||||
{!! Button::normal(trans('texts.view_in_gateway', ['gateway'=>$gatewayName]))
|
||||
->asLinkTo($gatewayLink)
|
||||
->withAttributes(['target' => '_blank']) !!}
|
||||
@endif
|
||||
|
||||
@if ($client->trashed())
|
||||
@ -115,13 +117,13 @@
|
||||
@if ($client->private_notes)
|
||||
<p><i>{{ $client->private_notes }}</i></p>
|
||||
@endif
|
||||
|
||||
|
||||
@if ($client->client_industry)
|
||||
{{ $client->client_industry->name }}<br/>
|
||||
@endif
|
||||
@if ($client->client_size)
|
||||
{{ $client->client_size->name }}<br/>
|
||||
@endif
|
||||
@endif
|
||||
|
||||
@if ($client->website)
|
||||
<p>{!! Utils::formatWebsite($client->website) !!}</p>
|
||||
@ -383,14 +385,14 @@
|
||||
|
||||
var map = new google.maps.Map(mapCanvas, mapOptions)
|
||||
var address = "{{ "{$client->address1} {$client->address2} {$client->city} {$client->state} {$client->postal_code} " . ($client->country ? $client->country->name : '') }}";
|
||||
|
||||
|
||||
geocoder = new google.maps.Geocoder();
|
||||
geocoder.geocode( { 'address': address}, function(results, status) {
|
||||
if (status == google.maps.GeocoderStatus.OK) {
|
||||
if (status != google.maps.GeocoderStatus.ZERO_RESULTS) {
|
||||
var result = results[0];
|
||||
map.setCenter(result.geometry.location);
|
||||
|
||||
|
||||
var infowindow = new google.maps.InfoWindow(
|
||||
{ content: '<b>'+result.formatted_address+'</b>',
|
||||
size: new google.maps.Size(150, 50)
|
||||
@ -398,9 +400,9 @@
|
||||
|
||||
var marker = new google.maps.Marker({
|
||||
position: result.geometry.location,
|
||||
map: map,
|
||||
map: map,
|
||||
title:address,
|
||||
});
|
||||
});
|
||||
google.maps.event.addListener(marker, 'click', function() {
|
||||
infowindow.open(map, marker);
|
||||
});
|
||||
|
@ -108,7 +108,7 @@
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@if ($account->isPro())
|
||||
@if ($account->hasFeature(FEATURE_DOCUMENTS))
|
||||
<div clas="row">
|
||||
<div class="col-md-2 col-sm-4"><div class="control-label" style="margin-bottom:10px;">{{trans('texts.expense_documents')}}</div></div>
|
||||
<div class="col-md-12 col-sm-8">
|
||||
|
@ -68,11 +68,11 @@
|
||||
background-color: {{ $color }} !important;
|
||||
}
|
||||
|
||||
.pagination>.active>a,
|
||||
.pagination>.active>span,
|
||||
.pagination>.active>a:hover,
|
||||
.pagination>.active>span:hover,
|
||||
.pagination>.active>a:focus,
|
||||
.pagination>.active>a,
|
||||
.pagination>.active>span,
|
||||
.pagination>.active>a:hover,
|
||||
.pagination>.active>span:hover,
|
||||
.pagination>.active>a:focus,
|
||||
.pagination>.active>span:focus {
|
||||
background-color: {{ $color }};
|
||||
border-color: {{ $color }};
|
||||
@ -161,14 +161,15 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (!empty($account->getTokenGatewayId()))
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<h3>{{ trans('texts.payment_methods') }}</h3>
|
||||
@include('payments.paymentmethods_list')
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div style="min-height: 550px">
|
||||
{!! Datatable::table()
|
||||
->addColumn(
|
||||
@ -185,4 +186,4 @@
|
||||
|
||||
</div>
|
||||
|
||||
@stop
|
||||
@stop
|
||||
|
@ -22,15 +22,17 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
@if (!empty($braintreeClientToken))
|
||||
@if ($accountGateway->gateway_id == GATEWAY_BRAINTREE && !empty($transactionToken))
|
||||
<div id="paypal-container"></div>
|
||||
<script type="text/javascript" src="https://js.braintreegateway.com/js/braintree-2.23.0.min.js"></script>
|
||||
<script type="text/javascript" >
|
||||
$(function() {
|
||||
var paypalLink = $('.dropdown-menu a[href$="/braintree_paypal"]'),
|
||||
var paypalLink = $('.dropdown-menu a[href$="paypal"]'),
|
||||
paypalUrl = paypalLink.attr('href'),
|
||||
checkout;
|
||||
console.log(paypalUrl);
|
||||
paypalLink.parent().attr('id', 'paypal-container');
|
||||
braintree.setup("{{ $braintreeClientToken }}", "custom", {
|
||||
braintree.setup("{{ $transactionToken }}", "custom", {
|
||||
onReady: function (integration) {
|
||||
checkout = integration;
|
||||
$('.dropdown-menu a[href$="#braintree_paypal"]').each(function(){
|
||||
@ -44,13 +46,13 @@
|
||||
enableShippingAddress: false,
|
||||
enableBillingAddress: false,
|
||||
headless: true,
|
||||
locale: "{{$invoice->client->language?$invoice->client->language->locale:$invoice->account->language->locale}}"
|
||||
locale: "{{ $invoice->client->language ? $invoice->client->language->locale : $invoice->account->language->locale }}"
|
||||
},
|
||||
dataCollector: {
|
||||
paypal: true
|
||||
},
|
||||
onPaymentMethodReceived: function (obj) {
|
||||
window.location.href = paypalUrl + '/' + encodeURIComponent(obj.nonce) + "?details=" + encodeURIComponent(JSON.stringify(obj.details))
|
||||
window.location.href = paypalUrl.replace('#braintree_paypal', '') + '/' + encodeURIComponent(obj.nonce) + "?device_data=" + encodeURIComponent(JSON.stringify(obj.details));
|
||||
}
|
||||
});
|
||||
paypalLink.click(function(e){
|
||||
@ -95,8 +97,8 @@
|
||||
|
||||
<div class="container">
|
||||
|
||||
@if ($checkoutComToken)
|
||||
@include('partials.checkout_com_payment')
|
||||
@if ($partialView)
|
||||
@include($partialView)
|
||||
@else
|
||||
<div class="pull-right" style="text-align:right">
|
||||
@if ($invoice->isQuote())
|
||||
@ -118,13 +120,14 @@
|
||||
@endif
|
||||
@endif
|
||||
</div>
|
||||
<div class="pull-left">
|
||||
@if(!empty($documentsZipURL))
|
||||
{!! Button::normal(trans('texts.download_documents', array('size'=>Form::human_filesize($documentsZipSize))))->asLinkTo($documentsZipURL)->large() !!}
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="pull-left">
|
||||
@if(!empty($documentsZipURL))
|
||||
{!! Button::normal(trans('texts.download_documents', array('size'=>Form::human_filesize($documentsZipSize))))->asLinkTo($documentsZipURL)->large() !!}
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="clearfix"></div><p> </p>
|
||||
@if ($account->isPro() && $invoice->hasDocuments())
|
||||
<div class="invoice-documents">
|
||||
|
@ -1,524 +0,0 @@
|
||||
@extends('public.header')
|
||||
|
||||
@section('head')
|
||||
@parent
|
||||
@if (!empty($braintreeClientToken))
|
||||
@include('payments.tokenization_braintree')
|
||||
@elseif (isset($accountGateway) && $accountGateway->getPublishableStripeKey())
|
||||
@include('payments.tokenization_stripe')
|
||||
@elseif (isset($accountGateway) && $accountGateway->gateway_id == GATEWAY_WEPAY && $paymentType != PAYMENT_TYPE_WEPAY_ACH)
|
||||
@include('payments.tokenization_wepay')
|
||||
@else
|
||||
<script type="text/javascript">
|
||||
$(function() {
|
||||
$('.payment-form').submit(function(event) {
|
||||
var $form = $(this);
|
||||
|
||||
// Disable the submit button to prevent repeated clicks
|
||||
$form.find('button').prop('disabled', true);
|
||||
|
||||
return true;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@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) }}
|
||||
|
||||
<script>
|
||||
$(function() {
|
||||
$('#card_number').val('4242424242424242');
|
||||
$('#cvv').val('1234');
|
||||
$('#expiration_month').val(1);
|
||||
$('#expiration_year').val({{ date_create()->modify('+3 year')->format('Y') }});
|
||||
})
|
||||
</script>
|
||||
@endif
|
||||
|
||||
|
||||
<div class="container">
|
||||
<p> </p>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-7">
|
||||
<header>
|
||||
@if ($client && isset($invoiceNumber))
|
||||
<h2>{{ $client->getDisplayName() }}</h2>
|
||||
<h3>{{ trans('texts.invoice') . ' ' . $invoiceNumber }}<span>| {{ trans('texts.amount_due') }}: <em>{{ $account->formatMoney($amount, $client, true) }}</em></span></h3>
|
||||
@elseif ($paymentTitle)
|
||||
<h2>{{ $paymentTitle }}
|
||||
@if(isset($paymentSubtitle))
|
||||
<br/><small>{{ $paymentSubtitle }}</small>
|
||||
@endif
|
||||
</h2>
|
||||
@endif
|
||||
</header>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
@if (Request::secure() || Utils::isNinjaDev())
|
||||
<div class="secure">
|
||||
<h3>{{ trans('texts.secure_payment') }}</h3>
|
||||
<div>{{ trans('texts.256_encryption') }}</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p> <br/> </p>
|
||||
<div>
|
||||
@if($paymentType != PAYMENT_TYPE_STRIPE_ACH && $paymentType != PAYMENT_TYPE_BRAINTREE_PAYPAL && $paymentType != PAYMENT_TYPE_WEPAY_ACH)
|
||||
<h3>{{ trans('texts.contact_information') }}</h3>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
{!! Former::text('first_name')
|
||||
->placeholder(trans('texts.first_name'))
|
||||
->label('') !!}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{!! Former::text('last_name')
|
||||
->placeholder(trans('texts.last_name'))
|
||||
->autocomplete('family-name')
|
||||
->label('') !!}
|
||||
</div>
|
||||
</div>
|
||||
@if (isset($paymentTitle) || ! empty($contact->email))
|
||||
<div class="row" style="display:{{ isset($paymentTitle) ? 'block' : 'none' }}">
|
||||
<div class="col-md-12">
|
||||
{!! Former::text('email')
|
||||
->placeholder(trans('texts.email'))
|
||||
->autocomplete('email')
|
||||
->label('') !!}
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<p> <br/> </p>
|
||||
|
||||
@if (!empty($showAddress))
|
||||
<h3>{{ trans('texts.billing_address') }} <span class="help">{{ trans('texts.payment_footer1') }}</span></h3>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
{!! Former::text('address1')
|
||||
->autocomplete('address-line1')
|
||||
->placeholder(trans('texts.address1'))
|
||||
->label('') !!}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{!! Former::text('address2')
|
||||
->autocomplete('address-line2')
|
||||
->placeholder(trans('texts.address2'))
|
||||
->label('') !!}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
{!! Former::text('city')
|
||||
->autocomplete('address-level2')
|
||||
->placeholder(trans('texts.city'))
|
||||
->label('') !!}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{!! Former::text('state')
|
||||
->autocomplete('address-level1')
|
||||
->placeholder(trans('texts.state'))
|
||||
->label('') !!}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
{!! Former::text('postal_code')
|
||||
->autocomplete('postal-code')
|
||||
->placeholder(trans('texts.postal_code'))
|
||||
->label('') !!}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{!! Former::select('country_id')
|
||||
->placeholder(trans('texts.country_id'))
|
||||
->fromQuery($countries, 'name', 'id')
|
||||
->addGroupClass('country-select')
|
||||
->label('') !!}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p> <br/> </p>
|
||||
@endif
|
||||
|
||||
<h3>{{ trans('texts.billing_method') }}</h3>
|
||||
@endif
|
||||
|
||||
|
||||
@if($paymentType == PAYMENT_TYPE_STRIPE_ACH)
|
||||
@if($accountGateway->getPlaidEnabled())
|
||||
<div id="plaid_container">
|
||||
<a class="btn btn-default btn-lg" id="plaid_link_button">
|
||||
<img src="{{ URL::to('images/plaid-logo.svg') }}">
|
||||
<img src="{{ URL::to('images/plaid-logowhite.svg') }}" class="hoverimg">
|
||||
{{ trans('texts.link_with_plaid') }}
|
||||
</a>
|
||||
<div id="plaid_linked">
|
||||
<div id="plaid_linked_status"></div>
|
||||
<a href="#" id="plaid_unlink">{{ trans('texts.unlink') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
<div id="manual_container">
|
||||
@if($accountGateway->getPlaidEnabled())
|
||||
<div id="plaid_or"><span>{{ trans('texts.or') }}</span></div>
|
||||
<h4>{{ trans('texts.link_manually') }}</h4>
|
||||
@endif
|
||||
<p>{{ trans('texts.ach_verification_delay_help') }}</p>
|
||||
{!! 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')) !!}
|
||||
<div class="form-group" style="margin-top:-15px">
|
||||
<div class="col-md-8 col-md-offset-4">
|
||||
<div id="bank_name"></div>
|
||||
</div>
|
||||
</div>
|
||||
{!! Former::text('')
|
||||
->id('account_number')
|
||||
->label(trans('texts.account_number')) !!}
|
||||
{!! Former::text('')
|
||||
->id('confirm_account_number')
|
||||
->label(trans('texts.confirm_account_number')) !!}
|
||||
</div>
|
||||
{!! Former::checkbox('authorize_ach')
|
||||
->text(trans('texts.ach_authorization', ['company'=>$account->getDisplayName(), 'email' => $account->work_email]))
|
||||
->label(' ') !!}
|
||||
<div class="col-md-8 col-md-offset-4">
|
||||
{!! 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
|
||||
</div>
|
||||
@elseif($paymentType == PAYMENT_TYPE_BRAINTREE_PAYPAL)
|
||||
<h3>{{ trans('texts.paypal') }}</h3>
|
||||
<div>{{$details->firstName}} {{$details->lastName}}</div>
|
||||
<div>{{$details->email}}</div>
|
||||
<input type="hidden" name="sourceToken" value="{{$sourceId}}">
|
||||
<input type="hidden" name="first_name" value="{{$details->firstName}}">
|
||||
<input type="hidden" name="last_name" value="{{$details->lastName}}">
|
||||
<input type="hidden" name="email" value="{{$details->email}}">
|
||||
<p> </p>
|
||||
@if (isset($amount) && $client && $account->showTokenCheckbox())
|
||||
<input id="token_billing" type="checkbox" name="token_billing" {{ $account->selectTokenCheckbox() ? 'CHECKED' : '' }} value="1" style="margin-left:0px; vertical-align:top">
|
||||
<label for="token_billing" class="checkbox" style="display: inline;">{{ trans('texts.token_billing_braintree_paypal') }}</label>
|
||||
<span class="help-block" style="font-size:15px">
|
||||
{!! trans('texts.token_billing_secure', ['link' => link_to('https://www.braintreepayments.com/', 'Braintree', ['target' => '_blank'])]) !!}
|
||||
</span>
|
||||
@endif
|
||||
<p> </p>
|
||||
<center>
|
||||
@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
|
||||
</center>
|
||||
@elseif($paymentType == PAYMENT_TYPE_WEPAY_ACH)
|
||||
<h3>{{ trans('texts.bank_account') }}</h3>
|
||||
@if (!empty($details))
|
||||
<div>{{$details->bank_account_name}}</div>
|
||||
<div>•••••{{$details->bank_account_last_four}}</div>
|
||||
@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' => '<a href="https://go.wepay.com/terms-of-service-us" target="_blank">'.trans('texts.terms_of_service').'</a>',
|
||||
'privacy_policy' => '<a href="https://go.wepay.com/privacy-policy-us" target="_blank">'.trans('texts.privacy_policy').'</a>',
|
||||
]))
|
||||
->help(trans('texts.payment_processed_through_wepay'))
|
||||
->label(' ') !!}
|
||||
<input type="hidden" name="sourceToken" value="{{$sourceId}}">
|
||||
<p> </p>
|
||||
<center>
|
||||
@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
|
||||
</center>
|
||||
@else
|
||||
<div class="row">
|
||||
<div class="col-md-9">
|
||||
@if (!empty($braintreeClientToken))
|
||||
<div id="card_number" class="braintree-hosted form-control"></div>
|
||||
@else
|
||||
{!! Former::text(!empty($tokenize) ? '' : 'card_number')
|
||||
->id('card_number')
|
||||
->placeholder(trans('texts.card_number'))
|
||||
->autocomplete('cc-number')
|
||||
->label('') !!}
|
||||
@endif
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
@if (!empty($braintreeClientToken))
|
||||
<div id="cvv" class="braintree-hosted form-control"></div>
|
||||
@else
|
||||
{!! Former::text(!empty($tokenize) ? '' : 'cvv')
|
||||
->id('cvv')
|
||||
->placeholder(trans('texts.cvv'))
|
||||
->autocomplete('off')
|
||||
->label('') !!}
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
@if (!empty($braintreeClientToken))
|
||||
<div id="expiration_month" class="braintree-hosted form-control"></div>
|
||||
@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
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
@if (!empty($braintreeClientToken))
|
||||
<div id="expiration_year" class="braintree-hosted form-control"></div>
|
||||
@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
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" style="padding-top:18px">
|
||||
<div class="col-md-5">
|
||||
@if (isset($amount) && $client && $account->showTokenCheckbox($storageGateway/* will contain gateway id */))
|
||||
<input id="token_billing" type="checkbox" name="token_billing" {{ $account->selectTokenCheckbox() ? 'CHECKED' : '' }} value="1" style="margin-left:0px; vertical-align:top">
|
||||
<label for="token_billing" class="checkbox" style="display: inline;">{{ trans('texts.token_billing') }}</label>
|
||||
<span class="help-block" style="font-size:15px">
|
||||
@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
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="col-md-7">
|
||||
@if (isset($acceptedCreditCardTypes))
|
||||
<div class="pull-right">
|
||||
@foreach ($acceptedCreditCardTypes as $card)
|
||||
<img src="{{ $card['source'] }}" alt="{{ $card['alt'] }}" style="width: 70px; display: inline; margin-right: 6px;"/>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p> </p>
|
||||
<center>
|
||||
@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
|
||||
</center>
|
||||
<p> </p>
|
||||
@endif
|
||||
|
||||
<div id="js-error-message" style="display:none" class="alert alert-danger"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<p> </p>
|
||||
<p> </p>
|
||||
|
||||
</div>
|
||||
|
||||
{!! Former::close() !!}
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
$(function() {
|
||||
$('select').change(function() {
|
||||
$(this).css({color:'#444444'});
|
||||
});
|
||||
|
||||
$('#country_id').combobox();
|
||||
$('#country').combobox();
|
||||
$('#currency').combobox();
|
||||
$('#first_name').focus();
|
||||
|
||||
@if($paymentType == PAYMENT_TYPE_STRIPE_ACH)
|
||||
var routingNumberCache = {};
|
||||
$('#routing_number, #country').on('change keypress keyup keydown paste', function(){setTimeout(function () {
|
||||
var routingNumber = $('#routing_number').val().replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
|
||||
|
||||
if (routingNumber.length != 9 || $("#country_id").val() != 840 || routingNumberCache[routingNumber] === false) {
|
||||
$('#bank_name').hide();
|
||||
} else if (routingNumberCache[routingNumber]) {
|
||||
$('#bank_name').empty().append(routingNumberCache[routingNumber]).show();
|
||||
} else {
|
||||
routingNumberCache[routingNumber] = false;
|
||||
$('#bank_name').hide();
|
||||
$.ajax({
|
||||
url:"{{ URL::to('/bank') }}/" + routingNumber,
|
||||
success:function(data) {
|
||||
var els = $().add(document.createTextNode(data.name + ", " + data.city + ", " + data.state));
|
||||
routingNumberCache[routingNumber] = els;
|
||||
|
||||
// Still the same number?
|
||||
if (routingNumber == $('#routing_number').val().replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '')) {
|
||||
$('#bank_name').empty().append(els).show();
|
||||
}
|
||||
},
|
||||
error:function(xhr) {
|
||||
if (xhr.status == 404) {
|
||||
var els = $(document.createTextNode('{{trans('texts.unknown_bank')}}'));
|
||||
;
|
||||
routingNumberCache[routingNumber] = els;
|
||||
|
||||
// Still the same number?
|
||||
if (routingNumber == $('#routing_number').val().replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '')) {
|
||||
$('#bank_name').empty().append(els).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
},10)})
|
||||
@endif
|
||||
});
|
||||
|
||||
</script>
|
||||
@if (isset($accountGateway) && $accountGateway->getPlaidEnabled())
|
||||
<a href="https://plaid.com/products/auth/" target="_blank" style="display:none" id="secured_by_plaid"><img src="{{ URL::to('images/plaid-logowhite.svg') }}">{{ trans('texts.secured_by_plaid') }}</a>
|
||||
<script src="https://cdn.plaid.com/link/stable/link-initialize.js"></script>
|
||||
@endif
|
||||
@stop
|
11
resources/views/payments/bank_transfer.blade.php
Normal file
11
resources/views/payments/bank_transfer.blade.php
Normal file
@ -0,0 +1,11 @@
|
||||
@extends('payments.payment_method')
|
||||
|
||||
@section('head')
|
||||
@parent
|
||||
|
||||
@if (isset($accountGateway) && $accountGateway->getPlaidEnabled())
|
||||
<a href="https://plaid.com/products/auth/" target="_blank" style="display:none" id="secured_by_plaid"><img src="{{ URL::to('images/plaid-logowhite.svg') }}">{{ trans('texts.secured_by_plaid') }}</a>
|
||||
<script src="https://cdn.plaid.com/link/stable/link-initialize.js"></script>
|
||||
@endif
|
||||
|
||||
@stop
|
73
resources/views/payments/braintree/credit_card.blade.php
Normal file
73
resources/views/payments/braintree/credit_card.blade.php
Normal file
@ -0,0 +1,73 @@
|
||||
@extends('payments.credit_card')
|
||||
|
||||
@section('head')
|
||||
@parent
|
||||
|
||||
<script type="text/javascript" src="https://js.braintreegateway.com/js/braintree-2.23.0.min.js"></script>
|
||||
<script type="text/javascript" >
|
||||
$(function() {
|
||||
var $form = $('.payment-form');
|
||||
braintree.setup("{{ $transactionToken }}", "custom", {
|
||||
id: "payment-form",
|
||||
hostedFields: {
|
||||
number: {
|
||||
selector: "#card_number",
|
||||
placeholder: "{{ trans('texts.card_number') }}"
|
||||
},
|
||||
cvv: {
|
||||
selector: "#cvv",
|
||||
placeholder: "{{ trans('texts.cvv') }}"
|
||||
},
|
||||
expirationMonth: {
|
||||
selector: "#expiration_month",
|
||||
placeholder: "{{ trans('texts.expiration_month') }}"
|
||||
},
|
||||
expirationYear: {
|
||||
selector: "#expiration_year",
|
||||
placeholder: "{{ trans('texts.expiration_year') }}"
|
||||
},
|
||||
styles: {
|
||||
'input': {
|
||||
'font-family': {!! json_encode(Utils::getFromCache($account->getBodyFontId(), 'fonts')['css_stack']) !!},
|
||||
'font-weight': "{{ Utils::getFromCache($account->getBodyFontId(), 'fonts')['css_weight'] }}",
|
||||
'font-size': '16px'
|
||||
}
|
||||
}
|
||||
},
|
||||
onError: function(e) {
|
||||
$form.find('button').prop('disabled', false);
|
||||
// Show the errors on the form
|
||||
if (e.details && e.details.invalidFieldKeys.length) {
|
||||
var invalidField = e.details.invalidFieldKeys[0];
|
||||
|
||||
if (invalidField == 'number') {
|
||||
$('#js-error-message').html('{{ trans('texts.invalid_card_number') }}').fadeIn();
|
||||
}
|
||||
else if (invalidField == 'expirationDate' || invalidField == 'expirationYear' || invalidField == 'expirationMonth') {
|
||||
$('#js-error-message').html('{{ trans('texts.invalid_expiry') }}').fadeIn();
|
||||
}
|
||||
else if (invalidField == 'cvv') {
|
||||
$('#js-error-message').html('{{ trans('texts.invalid_cvv') }}').fadeIn();
|
||||
}
|
||||
}
|
||||
else {
|
||||
$('#js-error-message').html(e.message).fadeIn();
|
||||
}
|
||||
},
|
||||
onPaymentMethodReceived: function(e) {
|
||||
// Insert the token into the form so it gets submitted to the server
|
||||
$form.append($('<input type="hidden" name="sourceToken"/>').val(e.nonce));
|
||||
// and submit
|
||||
$form.get(0).submit();
|
||||
}
|
||||
});
|
||||
$('.payment-form').submit(function(event) {
|
||||
var $form = $(this);
|
||||
|
||||
// Disable the submit button to prevent repeated clicks
|
||||
$form.find('button').prop('disabled', true);
|
||||
$('#js-error-message').hide();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@stop
|
42
resources/views/payments/braintree/paypal.blade.php
Normal file
42
resources/views/payments/braintree/paypal.blade.php
Normal file
@ -0,0 +1,42 @@
|
||||
@extends('payments.payment_method')
|
||||
|
||||
@section('payment_details')
|
||||
@parent
|
||||
|
||||
{!! Former::open($url) !!}
|
||||
|
||||
<h3>{{ trans('texts.paypal') }}</h3>
|
||||
|
||||
<div>{{$details->firstName}} {{$details->lastName}}</div>
|
||||
<div>{{$details->email}}</div>
|
||||
|
||||
<input type="hidden" name="sourceToken" value="{{$sourceId}}">
|
||||
<input type="hidden" name="first_name" value="{{$details->firstName}}">
|
||||
<input type="hidden" name="last_name" value="{{$details->lastName}}">
|
||||
<input type="hidden" name="email" value="{{$details->email}}">
|
||||
|
||||
<p> </p>
|
||||
|
||||
@if (isset($amount) && $client && $account->showTokenCheckbox())
|
||||
<input id="token_billing" type="checkbox" name="token_billing" {{ $account->selectTokenCheckbox() ? 'CHECKED' : '' }} value="1" style="margin-left:0px; vertical-align:top">
|
||||
<label for="token_billing" class="checkbox" style="display: inline;">{{ trans('texts.token_billing_braintree_paypal') }}</label>
|
||||
<span class="help-block" style="font-size:15px">
|
||||
{!! trans('texts.token_billing_secure', ['link' => link_to('https://www.braintreepayments.com/', 'Braintree', ['target' => '_blank'])]) !!}
|
||||
</span>
|
||||
@endif
|
||||
|
||||
<p> </p>
|
||||
|
||||
<center>
|
||||
@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
|
||||
</center>
|
||||
|
||||
@stop
|
@ -1,4 +1,4 @@
|
||||
@if ($checkoutComDebug)
|
||||
@if ($accountGateway->getConfigField('testMode'))
|
||||
<script src="https://sandbox.checkout.com/js/v1/checkout.js"></script>
|
||||
@else
|
||||
<script src="https://cdn.checkout.com/js/checkout.js"></script>
|
||||
@ -7,9 +7,9 @@
|
||||
<form method="POST" class="payment-form">
|
||||
<script>
|
||||
Checkout.render({
|
||||
debugMode: {{ $checkoutComDebug ? 'true' : 'false' }},
|
||||
publicKey: '{{ $checkoutComKey }}',
|
||||
paymentToken: '{{ $checkoutComToken }}',
|
||||
debugMode: {{ $accountGateway->getConfigField('testMode') ? 'true' : 'false' }},
|
||||
publicKey: '{{ $accountGateway->getConfigField('publicApiKey') }}',
|
||||
paymentToken: '{{ $transactionToken }}',
|
||||
customerEmail: '{{ $contact->email }}',
|
||||
customerName: '{{ $contact->getFullName() }}',
|
||||
value: {{ $invoice->getRequestedAmount() * 100 }},
|
||||
@ -19,8 +19,8 @@
|
||||
themeColor: '#3075dd',
|
||||
buttonColor:'#51c470',
|
||||
cardCharged: function(event){
|
||||
location.href = '{{ URL::to('/complete?token=' . $checkoutComToken) }}';
|
||||
location.href = '{{ URL::to('/complete/'. $invitation->invitation_key . '/credit_card?token=' . $transactionToken) }}';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</form>
|
||||
</form>
|
259
resources/views/payments/credit_card.blade.php
Normal file
259
resources/views/payments/credit_card.blade.php
Normal file
@ -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) }}
|
||||
|
||||
<script>
|
||||
$(function() {
|
||||
$('#card_number').val('4242424242424242');
|
||||
$('#cvv').val('1234');
|
||||
$('#expiration_month').val(1);
|
||||
$('#expiration_year').val({{ date_create()->modify('+3 year')->format('Y') }});
|
||||
})
|
||||
</script>
|
||||
@endif
|
||||
|
||||
<h3>{{ trans('texts.contact_information') }}</h3>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
{!! Former::text('first_name')
|
||||
->placeholder(trans('texts.first_name'))
|
||||
->label('') !!}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{!! Former::text('last_name')
|
||||
->placeholder(trans('texts.last_name'))
|
||||
->autocomplete('family-name')
|
||||
->label('') !!}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (isset($paymentTitle) || ! empty($contact->email))
|
||||
<div class="row" style="display:{{ isset($paymentTitle) ? 'block' : 'none' }}">
|
||||
<div class="col-md-12">
|
||||
{!! Former::text('email')
|
||||
->placeholder(trans('texts.email'))
|
||||
->autocomplete('email')
|
||||
->label('') !!}
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<p> <br/> </p>
|
||||
|
||||
@if (!empty($showAddress))
|
||||
<h3>{{ trans('texts.billing_address') }} <span class="help">{{ trans('texts.payment_footer1') }}</span></h3>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
{!! Former::text('address1')
|
||||
->autocomplete('address-line1')
|
||||
->placeholder(trans('texts.address1'))
|
||||
->label('') !!}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{!! Former::text('address2')
|
||||
->autocomplete('address-line2')
|
||||
->placeholder(trans('texts.address2'))
|
||||
->label('') !!}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
{!! Former::text('city')
|
||||
->autocomplete('address-level2')
|
||||
->placeholder(trans('texts.city'))
|
||||
->label('') !!}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{!! Former::text('state')
|
||||
->autocomplete('address-level1')
|
||||
->placeholder(trans('texts.state'))
|
||||
->label('') !!}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
{!! Former::text('postal_code')
|
||||
->autocomplete('postal-code')
|
||||
->placeholder(trans('texts.postal_code'))
|
||||
->label('') !!}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{!! Former::select('country_id')
|
||||
->placeholder(trans('texts.country_id'))
|
||||
->fromQuery(Cache::get('countries'), 'name', 'id')
|
||||
->addGroupClass('country-select')
|
||||
->label('') !!}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p> <br/> </p>
|
||||
@endif
|
||||
|
||||
<h3>{{ trans('texts.billing_method') }}</h3>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-9">
|
||||
@if ($accountGateway->gateway_id == GATEWAY_BRAINTREE)
|
||||
<div id="card_number" class="braintree-hosted form-control"></div>
|
||||
@else
|
||||
{!! Former::text(!empty($tokenize) ? '' : 'card_number')
|
||||
->id('card_number')
|
||||
->placeholder(trans('texts.card_number'))
|
||||
->autocomplete('cc-number')
|
||||
->label('') !!}
|
||||
@endif
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
@if ($accountGateway->gateway_id == GATEWAY_BRAINTREE)
|
||||
<div id="cvv" class="braintree-hosted form-control"></div>
|
||||
@else
|
||||
{!! Former::text(!empty($tokenize) ? '' : 'cvv')
|
||||
->id('cvv')
|
||||
->placeholder(trans('texts.cvv'))
|
||||
->autocomplete('off')
|
||||
->label('') !!}
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
@if ($accountGateway->gateway_id == GATEWAY_BRAINTREE)
|
||||
<div id="expiration_month" class="braintree-hosted form-control"></div>
|
||||
@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
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
@if ($accountGateway->gateway_id == GATEWAY_BRAINTREE)
|
||||
<div id="expiration_year" class="braintree-hosted form-control"></div>
|
||||
@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
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" style="padding-top:18px">
|
||||
<div class="col-md-5">
|
||||
@if (isset($amount) && $client && $account->showTokenCheckbox($storageGateway/* will contain gateway id */))
|
||||
<input id="token_billing" type="checkbox" name="token_billing" {{ $account->selectTokenCheckbox() ? 'CHECKED' : '' }} value="1" style="margin-left:0px; vertical-align:top">
|
||||
<label for="token_billing" class="checkbox" style="display: inline;">{{ trans('texts.token_billing') }}</label>
|
||||
<span class="help-block" style="font-size:15px">
|
||||
@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
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="col-md-7">
|
||||
@if (isset($acceptedCreditCardTypes))
|
||||
<div class="pull-right">
|
||||
@foreach ($acceptedCreditCardTypes as $card)
|
||||
<img src="{{ $card['source'] }}" alt="{{ $card['alt'] }}" style="width: 70px; display: inline; margin-right: 6px;"/>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-12">
|
||||
<div id="js-error-message" style="display:none" class="alert alert-danger"></div>
|
||||
</div>
|
||||
|
||||
<p> </p>
|
||||
<center>
|
||||
@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
|
||||
</center>
|
||||
<p> </p>
|
||||
|
||||
@stop
|
@ -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;
|
||||
|
74
resources/views/payments/payment_method.blade.php
Normal file
74
resources/views/payments/payment_method.blade.php
Normal file
@ -0,0 +1,74 @@
|
||||
@extends('public.header')
|
||||
|
||||
@section('content')
|
||||
|
||||
@include('payments.payment_css')
|
||||
|
||||
<div class="container">
|
||||
<p> </p>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-7">
|
||||
<header>
|
||||
@if ($client && isset($invoiceNumber))
|
||||
<h2>{{ $client->getDisplayName() }}</h2>
|
||||
<h3>{{ trans('texts.invoice') . ' ' . $invoiceNumber }}<span>| {{ trans('texts.amount_due') }}: <em>{{ $account->formatMoney($amount, $client, true) }}</em></span></h3>
|
||||
@elseif ($paymentTitle)
|
||||
<h2>{{ $paymentTitle }}
|
||||
@if(isset($paymentSubtitle))
|
||||
<br/><small>{{ $paymentSubtitle }}</small>
|
||||
@endif
|
||||
</h2>
|
||||
@endif
|
||||
</header>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
@if (Request::secure() || Utils::isNinjaDev())
|
||||
<div class="secure">
|
||||
<h3>{{ trans('texts.secure_payment') }}</h3>
|
||||
<div>{{ trans('texts.256_encryption') }}</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p> <br/> </p>
|
||||
|
||||
<div>
|
||||
|
||||
@yield('payment_details')
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<p> </p>
|
||||
<p> </p>
|
||||
|
||||
</div>
|
||||
|
||||
{!! Former::close() !!}
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
$(function() {
|
||||
$('select').change(function() {
|
||||
$(this).css({color:'#444444'});
|
||||
});
|
||||
|
||||
$('#country_id').combobox();
|
||||
$('#currency_id').combobox();
|
||||
$('#first_name').focus();
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
@stop
|
@ -16,6 +16,7 @@
|
||||
display:inline-block;
|
||||
}
|
||||
</style>
|
||||
|
||||
@if (!empty($braintreeClientToken))
|
||||
<script type="text/javascript" src="https://js.braintreegateway.com/js/braintree-2.23.0.min.js"></script>
|
||||
<script type="text/javascript" >
|
||||
@ -75,72 +76,84 @@
|
||||
});
|
||||
</script>
|
||||
@endif
|
||||
@if(!empty($paymentMethods))
|
||||
@foreach ($paymentMethods as $paymentMethod)
|
||||
<div class="payment_method">
|
||||
<span class="payment_method_img_container">
|
||||
<img height="22" src="{{URL::to('/images/credit_cards/'.str_replace(' ', '', strtolower($paymentMethod->payment_type->name).'.png'))}}" alt="{{trans("texts.card_" . str_replace(' ', '', strtolower($paymentMethod->payment_type->name)))}}">
|
||||
</span>
|
||||
@if(!empty($paymentMethod->last4))
|
||||
<span class="payment_method_number">•••••{{$paymentMethod->last4}}</span>
|
||||
@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)
|
||||
<a href="#" onclick="completeVerification('{{$paymentMethod->public_id}}','{{$paymentMethod->currency->symbol}}')">({{trans('texts.complete_verification')}})</a>
|
||||
|
||||
@if(!empty($paymentMethods) && count($paymentMethods))
|
||||
<h3>{{ trans('texts.payment_methods') }}</h3>
|
||||
|
||||
@foreach ($paymentMethods as $paymentMethod)
|
||||
<div class="payment_method">
|
||||
<span class="payment_method_img_container">
|
||||
<img height="22" src="{{ $paymentMethod->imageUrl() }}"
|
||||
alt="{{ trans("texts.card_" . str_replace(' ', '', strtolower($paymentMethod->payment_type->name))) }}">
|
||||
</span>
|
||||
|
||||
@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)
|
||||
<a href="#" onclick="completeVerification('{{$paymentMethod->public_id}}','{{$paymentMethod->currency->symbol}}')">({{trans('texts.complete_verification')}})</a>
|
||||
@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)
|
||||
<a href="#" onclick="setDefault('{{$paymentMethod->public_id}}')">({{trans('texts.use_for_auto_bill')}})</a>
|
||||
@endif
|
||||
<a href="#" class="payment_method_remove" onclick="removePaymentMethod('{{$paymentMethod->public_id}}')">×</a>
|
||||
</div>
|
||||
@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)
|
||||
<a href="#" onclick="setDefault('{{$paymentMethod->public_id}}')">({{trans('texts.use_for_auto_bill')}})</a>
|
||||
@endif
|
||||
<a href="#" title="{{ trans('texts.remove') }}" class="payment_method_remove" onclick="removePaymentMethod('{{$paymentMethod->public_id}}')">×</a>
|
||||
|
||||
</div>
|
||||
@endforeach
|
||||
@endif
|
||||
|
||||
@if($gateway->gateway_id != GATEWAY_STRIPE || $gateway->getPublishableStripeKey())
|
||||
|
||||
<center>
|
||||
{!! 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')) !!}
|
||||
<div id="paypal-container"></div>
|
||||
@endif
|
||||
</center>
|
||||
@endif
|
||||
|
||||
<div class="modal fade" id="completeVerificationModal" tabindex="-1" role="dialog" aria-labelledby="completeVerificationModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" style="min-width:150px">
|
||||
<div class="modal-content">
|
||||
{!! Former::open('/client/paymentmethods/verify') !!}
|
||||
{!! Former::open('/client/payment_methods/verify') !!}
|
||||
|
||||
@if (Utils::isNinjaDev())
|
||||
<script>
|
||||
$(function() {
|
||||
$('#verification1').val('32');
|
||||
$('#verification2').val('45');
|
||||
})
|
||||
</script>
|
||||
@endif
|
||||
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title" id="completeVerificationModalLabel">{{ trans('texts.complete_verification') }}</h4>
|
||||
@ -199,8 +212,9 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{!! Former::open(URL::to('/client/paymentmethods/default'))->id('defaultSourceForm') !!}
|
||||
<input type="hidden" name="source" id="default_id">
|
||||
|
||||
{!! Former::open(URL::to('/client/payment_methods/default'))->id('defaultSourceForm') !!}
|
||||
<input type="hidden" name="source" id="default_id">
|
||||
{!! Former::close() !!}
|
||||
|
||||
<script type="text/javascript">
|
||||
@ -212,7 +226,7 @@
|
||||
}
|
||||
|
||||
function removePaymentMethod(sourceId) {
|
||||
$('#removeForm').attr('action', '{{ URL::to('/client/paymentmethods/%s/remove') }}'.replace('%s', sourceId))
|
||||
$('#removeForm').attr('action', '{{ URL::to('/client/payment_methods/%s/remove') }}'.replace('%s', sourceId))
|
||||
$('#removePaymentMethodModal').modal('show');
|
||||
}
|
||||
|
||||
|
285
resources/views/payments/stripe/bank_transfer.blade.php
Normal file
285
resources/views/payments/stripe/bank_transfer.blade.php
Normal file
@ -0,0 +1,285 @@
|
||||
@extends('payments.bank_transfer')
|
||||
|
||||
@section('head')
|
||||
@parent
|
||||
|
||||
<script type="text/javascript" src="https://js.stripe.com/v2/"></script>
|
||||
<script type="text/javascript">
|
||||
Stripe.setPublishableKey('{{ $accountGateway->getPublishableStripeKey() }}');
|
||||
$(function() {
|
||||
var countries = {!! Cache::get('countries')->pluck('iso_3166_2','id') !!};
|
||||
$('.payment-form').submit(function(event) {
|
||||
if($('[name=plaidAccountId]').length)return;
|
||||
|
||||
var $form = $(this);
|
||||
|
||||
var data = {
|
||||
account_holder_name: $('#account_holder_name').val(),
|
||||
account_holder_type: $('[name=account_holder_type]:checked').val(),
|
||||
currency: $("#currency_id").val(),
|
||||
country: countries[$("#country_id").val()],
|
||||
routing_number: $('#routing_number').val().replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ''),
|
||||
account_number: $('#account_number').val().replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '')
|
||||
};
|
||||
|
||||
// Validate the account details
|
||||
if (!data.account_holder_type) {
|
||||
$('#js-error-message').html('{{ trans('texts.missing_account_holder_type') }}').fadeIn();
|
||||
return false;
|
||||
}
|
||||
if (!data.account_holder_name) {
|
||||
$('#js-error-message').html('{{ trans('texts.missing_account_holder_name') }}').fadeIn();
|
||||
return false;
|
||||
}
|
||||
if (!data.routing_number || !Stripe.bankAccount.validateRoutingNumber(data.routing_number, data.country)) {
|
||||
$('#js-error-message').html('{{ trans('texts.invalid_routing_number') }}').fadeIn();
|
||||
return false;
|
||||
}
|
||||
if (data.account_number != $('#confirm_account_number').val().replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '')) {
|
||||
$('#js-error-message').html('{{ trans('texts.account_number_mismatch') }}').fadeIn();
|
||||
return false;
|
||||
}
|
||||
if (!data.account_number || !Stripe.bankAccount.validateAccountNumber(data.account_number, data.country)) {
|
||||
$('#js-error-message').html('{{ trans('texts.invalid_account_number') }}').fadeIn();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Disable the submit button to prevent repeated clicks
|
||||
$form.find('button').prop('disabled', true);
|
||||
$('#js-error-message').hide();
|
||||
|
||||
Stripe.bankAccount.createToken(data, stripeResponseHandler);
|
||||
|
||||
// Prevent the form from submitting with the default action
|
||||
return false;
|
||||
});
|
||||
|
||||
@if ($accountGateway->getPlaidEnabled())
|
||||
var plaidHandler = Plaid.create({
|
||||
selectAccount: true,
|
||||
env: '{{ $accountGateway->getPlaidEnvironment() }}',
|
||||
clientName: {!! json_encode($account->getDisplayName()) !!},
|
||||
key: '{{ $accountGateway->getPlaidPublicKey() }}',
|
||||
product: 'auth',
|
||||
onSuccess: plaidSuccessHandler,
|
||||
onExit : function(){$('#secured_by_plaid').hide()}
|
||||
});
|
||||
|
||||
$('#plaid_link_button').click(function(){plaidHandler.open();$('#secured_by_plaid').fadeIn()});
|
||||
$('#plaid_unlink').click(function(e){
|
||||
e.preventDefault();
|
||||
$('#manual_container').fadeIn();
|
||||
$('#plaid_linked').hide();
|
||||
$('#plaid_link_button').show();
|
||||
$('#pay_now_button').hide();
|
||||
$('#add_account_button').show();
|
||||
$('[name=plaidPublicToken]').remove();
|
||||
$('[name=plaidAccountId]').remove();
|
||||
$('[name=account_holder_type],#account_holder_name').attr('required','required');
|
||||
})
|
||||
@endif
|
||||
});
|
||||
|
||||
function stripeResponseHandler(status, response) {
|
||||
var $form = $('.payment-form');
|
||||
|
||||
if (response.error) {
|
||||
// Show the errors on the form
|
||||
var error = response.error.message;
|
||||
if(response.error.param == 'bank_account[country]') {
|
||||
error = "{{trans('texts.country_not_supported')}}";
|
||||
}
|
||||
$form.find('button').prop('disabled', false);
|
||||
$('#js-error-message').html(error).fadeIn();
|
||||
} else {
|
||||
// response contains id and card, which contains additional card details
|
||||
var token = response.id;
|
||||
// Insert the token into the form so it gets submitted to the server
|
||||
$form.append($('<input type="hidden" name="sourceToken"/>').val(token));
|
||||
// and submit
|
||||
$form.get(0).submit();
|
||||
}
|
||||
};
|
||||
|
||||
function plaidSuccessHandler(public_token, metadata) {
|
||||
$('#secured_by_plaid').hide()
|
||||
var $form = $('.payment-form');
|
||||
|
||||
$form.append($('<input type="hidden" name="plaidPublicToken"/>').val(public_token));
|
||||
$form.append($('<input type="hidden" name="plaidAccountId"/>').val(metadata.account_id));
|
||||
$('#plaid_linked_status').text('{{ trans('texts.plaid_linked_status') }}'.replace(':bank', metadata.institution.name));
|
||||
$('#manual_container').fadeOut();
|
||||
$('#plaid_linked').show();
|
||||
$('#plaid_link_button').hide();
|
||||
$('[name=account_holder_type],#account_holder_name').removeAttr('required');
|
||||
|
||||
|
||||
var payNowBtn = $('#pay_now_button');
|
||||
if(payNowBtn.length) {
|
||||
payNowBtn.show();
|
||||
$('#add_account_button').hide();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@stop
|
||||
|
||||
|
||||
@section('payment_details')
|
||||
|
||||
@if ($accountGateway->getPlaidEnabled())
|
||||
<div id="plaid_container">
|
||||
<a class="btn btn-default btn-lg" id="plaid_link_button">
|
||||
<img src="{{ URL::to('images/plaid-logo.svg') }}">
|
||||
<img src="{{ URL::to('images/plaid-logowhite.svg') }}" class="hoverimg">
|
||||
{{ trans('texts.link_with_plaid') }}
|
||||
</a>
|
||||
<div id="plaid_linked">
|
||||
<div id="plaid_linked_status"></div>
|
||||
<a href="#" id="plaid_unlink">{{ trans('texts.unlink') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div id="manual_container">
|
||||
@if($accountGateway->getPlaidEnabled())
|
||||
<div id="plaid_or"><span>{{ trans('texts.or') }}</span></div>
|
||||
<h4>{{ trans('texts.link_manually') }}</h4>
|
||||
@endif
|
||||
|
||||
<p>{{ trans('texts.ach_verification_delay_help') }}</p><br/>
|
||||
|
||||
{!! 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') !!}
|
||||
<script>
|
||||
$(function() {
|
||||
$('#routing_number').val('110000000');
|
||||
$('#account_number').val('000123456789');
|
||||
$('#confirm_account_number').val('000123456789');
|
||||
$('#authorize_ach').prop('checked', true);
|
||||
})
|
||||
</script>
|
||||
@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')) !!}
|
||||
|
||||
<div class="form-group" style="margin-top:-15px">
|
||||
<div class="col-md-8 col-md-offset-4">
|
||||
<div id="bank_name"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!! Former::text('')
|
||||
->id('account_number')
|
||||
->label(trans('texts.account_number')) !!}
|
||||
{!! Former::text('')
|
||||
->id('confirm_account_number')
|
||||
->label(trans('texts.confirm_account_number')) !!}
|
||||
</div>
|
||||
|
||||
{!! Former::checkbox('authorize_ach')
|
||||
->text(trans('texts.ach_authorization', ['company'=>$account->getDisplayName(), 'email' => $account->work_email]))
|
||||
->label(' ') !!}
|
||||
|
||||
|
||||
<div class="col-md-12">
|
||||
<div id="js-error-message" style="display:none" class="alert alert-danger"></div>
|
||||
</div>
|
||||
|
||||
<p> </p>
|
||||
|
||||
<div class="col-md-8 col-md-offset-4">
|
||||
|
||||
{!! 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
|
||||
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
var routingNumberCache = {};
|
||||
$('#routing_number, #country').on('change keypress keyup keydown paste', function(){setTimeout(function () {
|
||||
var routingNumber = $('#routing_number').val().replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
|
||||
if (routingNumber.length != 9 || $("#country_id").val() != 840 || routingNumberCache[routingNumber] === false) {
|
||||
$('#bank_name').hide();
|
||||
} else if (routingNumberCache[routingNumber]) {
|
||||
$('#bank_name').empty().append(routingNumberCache[routingNumber]).show();
|
||||
} else {
|
||||
routingNumberCache[routingNumber] = false;
|
||||
$('#bank_name').hide();
|
||||
$.ajax({
|
||||
url:"{{ URL::to('/bank') }}/" + routingNumber,
|
||||
success:function(data) {
|
||||
var els = $().add(document.createTextNode(data.name + ", " + data.city + ", " + data.state));
|
||||
routingNumberCache[routingNumber] = els;
|
||||
|
||||
// Still the same number?
|
||||
if (routingNumber == $('#routing_number').val().replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '')) {
|
||||
$('#bank_name').empty().append(els).show();
|
||||
}
|
||||
},
|
||||
error:function(xhr) {
|
||||
if (xhr.status == 404) {
|
||||
var els = $(document.createTextNode('{{trans('texts.unknown_bank')}}'));
|
||||
routingNumberCache[routingNumber] = els;
|
||||
|
||||
// Still the same number?
|
||||
if (routingNumber == $('#routing_number').val().replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '')) {
|
||||
$('#bank_name').empty().append(els).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
},10)})
|
||||
</script>
|
||||
|
||||
@stop
|
89
resources/views/payments/stripe/credit_card.blade.php
Normal file
89
resources/views/payments/stripe/credit_card.blade.php
Normal file
@ -0,0 +1,89 @@
|
||||
@extends('payments.credit_card')
|
||||
|
||||
@section('head')
|
||||
@parent
|
||||
|
||||
@if ($accountGateway->getPublishableStripeKey())
|
||||
<script type="text/javascript" src="https://js.stripe.com/v2/"></script>
|
||||
<script type="text/javascript">
|
||||
Stripe.setPublishableKey('{{ $accountGateway->getPublishableStripeKey() }}');
|
||||
$(function() {
|
||||
var countries = {!! Cache::get('countries')->pluck('iso_3166_2','id') !!};
|
||||
$('.payment-form').submit(function(event) {
|
||||
if($('[name=plaidAccountId]').length)return;
|
||||
|
||||
var $form = $(this);
|
||||
|
||||
var data = {
|
||||
name: $('#first_name').val() + ' ' + $('#last_name').val(),
|
||||
address_line1: $('#address1').val(),
|
||||
address_line2: $('#address2').val(),
|
||||
address_city: $('#city').val(),
|
||||
address_state: $('#state').val(),
|
||||
address_zip: $('#postal_code').val(),
|
||||
address_country: $("#country_id option:selected").text(),
|
||||
number: $('#card_number').val(),
|
||||
cvc: $('#cvv').val(),
|
||||
exp_month: $('#expiration_month').val(),
|
||||
exp_year: $('#expiration_year').val()
|
||||
};
|
||||
|
||||
// Validate the card details
|
||||
if (!Stripe.card.validateCardNumber(data.number)) {
|
||||
$('#js-error-message').html('{{ trans('texts.invalid_card_number') }}').fadeIn();
|
||||
return false;
|
||||
}
|
||||
if (!Stripe.card.validateExpiry(data.exp_month, data.exp_year)) {
|
||||
$('#js-error-message').html('{{ trans('texts.invalid_expiry') }}').fadeIn();
|
||||
return false;
|
||||
}
|
||||
if (!Stripe.card.validateCVC(data.cvc)) {
|
||||
$('#js-error-message').html('{{ trans('texts.invalid_cvv') }}').fadeIn();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Disable the submit button to prevent repeated clicks
|
||||
$form.find('button').prop('disabled', true);
|
||||
$('#js-error-message').hide();
|
||||
|
||||
Stripe.card.createToken(data, stripeResponseHandler);
|
||||
|
||||
// Prevent the form from submitting with the default action
|
||||
return false;
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
function stripeResponseHandler(status, response) {
|
||||
var $form = $('.payment-form');
|
||||
|
||||
if (response.error) {
|
||||
// Show the errors on the form
|
||||
var error = response.error.message;
|
||||
$form.find('button').prop('disabled', false);
|
||||
$('#js-error-message').html(error).fadeIn();
|
||||
} else {
|
||||
// response contains id and card, which contains additional card details
|
||||
var token = response.id;
|
||||
// Insert the token into the form so it gets submitted to the server
|
||||
$form.append($('<input type="hidden" name="sourceToken"/>').val(token));
|
||||
// and submit
|
||||
$form.get(0).submit();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@else
|
||||
<script type="text/javascript">
|
||||
$(function() {
|
||||
$('.payment-form').submit(function(event) {
|
||||
var $form = $(this);
|
||||
|
||||
// Disable the submit button to prevent repeated clicks
|
||||
$form.find('button').prop('disabled', true);
|
||||
|
||||
return true;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endif
|
||||
@stop
|
@ -1,67 +0,0 @@
|
||||
<script type="text/javascript" src="https://js.braintreegateway.com/js/braintree-2.23.0.min.js"></script>
|
||||
<script type="text/javascript" >
|
||||
$(function() {
|
||||
var $form = $('.payment-form');
|
||||
braintree.setup("{{ $braintreeClientToken }}", "custom", {
|
||||
id: "payment-form",
|
||||
hostedFields: {
|
||||
number: {
|
||||
selector: "#card_number",
|
||||
placeholder: "{{ trans('texts.card_number') }}"
|
||||
},
|
||||
cvv: {
|
||||
selector: "#cvv",
|
||||
placeholder: "{{ trans('texts.cvv') }}"
|
||||
},
|
||||
expirationMonth: {
|
||||
selector: "#expiration_month",
|
||||
placeholder: "{{ trans('texts.expiration_month') }}"
|
||||
},
|
||||
expirationYear: {
|
||||
selector: "#expiration_year",
|
||||
placeholder: "{{ trans('texts.expiration_year') }}"
|
||||
},
|
||||
styles: {
|
||||
'input': {
|
||||
'font-family': {!! json_encode(Utils::getFromCache($account->getBodyFontId(), 'fonts')['css_stack']) !!},
|
||||
'font-weight': "{{ Utils::getFromCache($account->getBodyFontId(), 'fonts')['css_weight'] }}",
|
||||
'font-size': '16px'
|
||||
}
|
||||
}
|
||||
},
|
||||
onError: function(e) {
|
||||
$form.find('button').prop('disabled', false);
|
||||
// Show the errors on the form
|
||||
if (e.details && e.details.invalidFieldKeys.length) {
|
||||
var invalidField = e.details.invalidFieldKeys[0];
|
||||
|
||||
if (invalidField == 'number') {
|
||||
$('#js-error-message').html('{{ trans('texts.invalid_card_number') }}').fadeIn();
|
||||
}
|
||||
else if (invalidField == 'expirationDate' || invalidField == 'expirationYear' || invalidField == 'expirationMonth') {
|
||||
$('#js-error-message').html('{{ trans('texts.invalid_expiry') }}').fadeIn();
|
||||
}
|
||||
else if (invalidField == 'cvv') {
|
||||
$('#js-error-message').html('{{ trans('texts.invalid_cvv') }}').fadeIn();
|
||||
}
|
||||
}
|
||||
else {
|
||||
$('#js-error-message').html(e.message).fadeIn();
|
||||
}
|
||||
},
|
||||
onPaymentMethodReceived: function(e) {
|
||||
// Insert the token into the form so it gets submitted to the server
|
||||
$form.append($('<input type="hidden" name="sourceToken"/>').val(e.nonce));
|
||||
// and submit
|
||||
$form.get(0).submit();
|
||||
}
|
||||
});
|
||||
$('.payment-form').submit(function(event) {
|
||||
var $form = $(this);
|
||||
|
||||
// Disable the submit button to prevent repeated clicks
|
||||
$form.find('button').prop('disabled', true);
|
||||
$('#js-error-message').hide();
|
||||
});
|
||||
});
|
||||
</script>
|
@ -1,154 +0,0 @@
|
||||
<script type="text/javascript" src="https://js.stripe.com/v2/"></script>
|
||||
<script type="text/javascript">
|
||||
Stripe.setPublishableKey('{{ $accountGateway->getPublishableStripeKey() }}');
|
||||
$(function() {
|
||||
var countries = {!! $countries->pluck('iso_3166_2','id') !!};
|
||||
$('.payment-form').submit(function(event) {
|
||||
if($('[name=plaidAccountId]').length)return;
|
||||
|
||||
var $form = $(this);
|
||||
|
||||
var data = {
|
||||
@if($paymentType == PAYMENT_TYPE_STRIPE_ACH)
|
||||
account_holder_name: $('#account_holder_name').val(),
|
||||
account_holder_type: $('[name=account_holder_type]:checked').val(),
|
||||
currency: $("#currency").val(),
|
||||
country: countries[$("#country_id").val()],
|
||||
routing_number: $('#routing_number').val().replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ''),
|
||||
account_number: $('#account_number').val().replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '')
|
||||
@else
|
||||
name: $('#first_name').val() + ' ' + $('#last_name').val(),
|
||||
address_line1: $('#address1').val(),
|
||||
address_line2: $('#address2').val(),
|
||||
address_city: $('#city').val(),
|
||||
address_state: $('#state').val(),
|
||||
address_zip: $('#postal_code').val(),
|
||||
address_country: $("#country_id option:selected").text(),
|
||||
number: $('#card_number').val(),
|
||||
cvc: $('#cvv').val(),
|
||||
exp_month: $('#expiration_month').val(),
|
||||
exp_year: $('#expiration_year').val()
|
||||
@endif
|
||||
};
|
||||
|
||||
@if($paymentType == PAYMENT_TYPE_STRIPE_ACH)
|
||||
// Validate the account details
|
||||
if (!data.account_holder_type) {
|
||||
$('#js-error-message').html('{{ trans('texts.missing_account_holder_type') }}').fadeIn();
|
||||
return false;
|
||||
}
|
||||
if (!data.account_holder_name) {
|
||||
$('#js-error-message').html('{{ trans('texts.missing_account_holder_name') }}').fadeIn();
|
||||
return false;
|
||||
}
|
||||
if (!data.routing_number || !Stripe.bankAccount.validateRoutingNumber(data.routing_number, data.country)) {
|
||||
$('#js-error-message').html('{{ trans('texts.invalid_routing_number') }}').fadeIn();
|
||||
return false;
|
||||
}
|
||||
if (data.account_number != $('#confirm_account_number').val().replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '')) {
|
||||
$('#js-error-message').html('{{ trans('texts.account_number_mismatch') }}').fadeIn();
|
||||
return false;
|
||||
}
|
||||
if (!data.account_number || !Stripe.bankAccount.validateAccountNumber(data.account_number, data.country)) {
|
||||
$('#js-error-message').html('{{ trans('texts.invalid_account_number') }}').fadeIn();
|
||||
return false;
|
||||
}
|
||||
@else
|
||||
// Validate the card details
|
||||
if (!Stripe.card.validateCardNumber(data.number)) {
|
||||
$('#js-error-message').html('{{ trans('texts.invalid_card_number') }}').fadeIn();
|
||||
return false;
|
||||
}
|
||||
if (!Stripe.card.validateExpiry(data.exp_month, data.exp_year)) {
|
||||
$('#js-error-message').html('{{ trans('texts.invalid_expiry') }}').fadeIn();
|
||||
return false;
|
||||
}
|
||||
if (!Stripe.card.validateCVC(data.cvc)) {
|
||||
$('#js-error-message').html('{{ trans('texts.invalid_cvv') }}').fadeIn();
|
||||
return false;
|
||||
}
|
||||
@endif
|
||||
|
||||
// Disable the submit button to prevent repeated clicks
|
||||
$form.find('button').prop('disabled', true);
|
||||
$('#js-error-message').hide();
|
||||
|
||||
@if($paymentType == PAYMENT_TYPE_STRIPE_ACH)
|
||||
Stripe.bankAccount.createToken(data, stripeResponseHandler);
|
||||
@else
|
||||
Stripe.card.createToken(data, stripeResponseHandler);
|
||||
@endif
|
||||
|
||||
// Prevent the form from submitting with the default action
|
||||
return false;
|
||||
});
|
||||
|
||||
@if($accountGateway->getPlaidEnabled())
|
||||
var plaidHandler = Plaid.create({
|
||||
selectAccount: true,
|
||||
env: '{{ $accountGateway->getPlaidEnvironment() }}',
|
||||
clientName: {!! json_encode($account->getDisplayName()) !!},
|
||||
key: '{{ $accountGateway->getPlaidPublicKey() }}',
|
||||
product: 'auth',
|
||||
onSuccess: plaidSuccessHandler,
|
||||
onExit : function(){$('#secured_by_plaid').hide()}
|
||||
});
|
||||
|
||||
$('#plaid_link_button').click(function(){plaidHandler.open();$('#secured_by_plaid').fadeIn()});
|
||||
$('#plaid_unlink').click(function(e){
|
||||
e.preventDefault();
|
||||
$('#manual_container').fadeIn();
|
||||
$('#plaid_linked').hide();
|
||||
$('#plaid_link_button').show();
|
||||
$('#pay_now_button').hide();
|
||||
$('#add_account_button').show();
|
||||
$('[name=plaidPublicToken]').remove();
|
||||
$('[name=plaidAccountId]').remove();
|
||||
$('[name=account_holder_type],#account_holder_name').attr('required','required');
|
||||
})
|
||||
@endif
|
||||
});
|
||||
|
||||
function stripeResponseHandler(status, response) {
|
||||
var $form = $('.payment-form');
|
||||
|
||||
if (response.error) {
|
||||
// Show the errors on the form
|
||||
var error = response.error.message;
|
||||
@if($paymentType == PAYMENT_TYPE_STRIPE_ACH)
|
||||
if(response.error.param == 'bank_account[country]') {
|
||||
error = "{{trans('texts.country_not_supported')}}";
|
||||
}
|
||||
@endif
|
||||
$form.find('button').prop('disabled', false);
|
||||
$('#js-error-message').html(error).fadeIn();
|
||||
} else {
|
||||
// response contains id and card, which contains additional card details
|
||||
var token = response.id;
|
||||
// Insert the token into the form so it gets submitted to the server
|
||||
$form.append($('<input type="hidden" name="sourceToken"/>').val(token));
|
||||
// and submit
|
||||
$form.get(0).submit();
|
||||
}
|
||||
};
|
||||
|
||||
function plaidSuccessHandler(public_token, metadata) {
|
||||
$('#secured_by_plaid').hide()
|
||||
var $form = $('.payment-form');
|
||||
|
||||
$form.append($('<input type="hidden" name="plaidPublicToken"/>').val(public_token));
|
||||
$form.append($('<input type="hidden" name="plaidAccountId"/>').val(metadata.account_id));
|
||||
$('#plaid_linked_status').text('{{ trans('texts.plaid_linked_status') }}'.replace(':bank', metadata.institution.name));
|
||||
$('#manual_container').fadeOut();
|
||||
$('#plaid_linked').show();
|
||||
$('#plaid_link_button').hide();
|
||||
$('[name=account_holder_type],#account_holder_name').removeAttr('required');
|
||||
|
||||
|
||||
var payNowBtn = $('#pay_now_button');
|
||||
if(payNowBtn.length) {
|
||||
payNowBtn.show();
|
||||
$('#add_account_button').hide();
|
||||
}
|
||||
};
|
||||
</script>
|
40
resources/views/payments/wepay/bank_transfer.blade.php
Normal file
40
resources/views/payments/wepay/bank_transfer.blade.php
Normal file
@ -0,0 +1,40 @@
|
||||
@extends('payments.bank_transfer')
|
||||
|
||||
@section('payment_details')
|
||||
|
||||
<h3>{{ trans('texts.bank_account') }}</h3>
|
||||
|
||||
@if (!empty($details))
|
||||
<div>{{$details->bank_account_name}}</div>
|
||||
<div>•••••{{$details->bank_account_last_four}}</div>
|
||||
@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' => '<a href="https://go.wepay.com/terms-of-service-us" target="_blank">'.trans('texts.terms_of_service').'</a>',
|
||||
'privacy_policy' => '<a href="https://go.wepay.com/privacy-policy-us" target="_blank">'.trans('texts.privacy_policy').'</a>',
|
||||
]))
|
||||
->help(trans('texts.payment_processed_through_wepay'))
|
||||
->label(' ') !!}
|
||||
|
||||
<input type="hidden" name="sourceToken" value="{{$sourceId}}">
|
||||
|
||||
<p> </p>
|
||||
|
||||
<center>
|
||||
@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
|
||||
</center>
|
||||
|
||||
@stop
|
@ -69,7 +69,7 @@
|
||||
@if (!isset($account) || !$account->hasFeature(FEATURE_WHITE_LABEL))
|
||||
{{-- Per our license, please do not remove or modify this link. --}}
|
||||
<a class="navbar-brand" href="{{ URL::to(NINJA_WEB_URL) }}" target="_blank"><img src="{{ asset('images/invoiceninja-logo.png') }}" style="height:20px"></a>
|
||||
@endif
|
||||
@endif
|
||||
</div>
|
||||
<div id="navbar" class="collapse navbar-collapse">
|
||||
@if (!isset($account) || $account->isNinjaAccount() || $account->enable_client_portal)
|
||||
@ -91,13 +91,13 @@
|
||||
</li>
|
||||
@endif
|
||||
@if (isset($account) && $account->getTokenGatewayId() && !$account->enable_client_portal_dashboard)
|
||||
<li {{ Request::is('*client/paymentmethods') ? 'class="active"' : '' }}>
|
||||
{!! link_to('/client/paymentmethods', trans('texts.payment_methods') ) !!}
|
||||
<li {{ Request::is('*client/payment_methods') ? 'class="active"' : '' }}>
|
||||
{!! link_to('/client/payment_methods', trans('texts.payment_methods') ) !!}
|
||||
</li>
|
||||
@endif
|
||||
<li {{ Request::is('*client/payments') ? 'class="active"' : '' }}>
|
||||
{!! link_to('/client/payments', trans('texts.payments') ) !!}
|
||||
</li>
|
||||
</li>
|
||||
</ul>
|
||||
@endif
|
||||
</div><!--/.nav-collapse -->
|
||||
@ -137,16 +137,16 @@
|
||||
</li>
|
||||
<li id="menu-item-33" class="menu-item-33">
|
||||
{!! link_to('#', 'GitHub', ['target' => '_blank', 'onclick' => 'openUrl("https://github.com/hillelcoren/invoice-ninja", "/footer/social/github")']) !!}
|
||||
</li>
|
||||
</li>
|
||||
<li id="menu-item-30" class="menu-item-30">
|
||||
{!! link_to(NINJA_WEB_URL . '/contact', trans('texts.contact')) !!}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
</div><!-- .wrap -->
|
||||
</div><!-- .top -->
|
||||
|
||||
|
||||
<div class="bottom">
|
||||
<div class="wrap">
|
||||
@if (!isset($account) || !$account->hasFeature(FEATURE_WHITE_LABEL))
|
||||
|
Loading…
x
Reference in New Issue
Block a user