From 88ec714cd561bc4cf3613f975170b556499f8428 Mon Sep 17 00:00:00 2001 From: Joshua Dwire Date: Tue, 10 May 2016 18:46:32 -0400 Subject: [PATCH] Store Stripe card summary locally --- app/Http/Controllers/PaymentController.php | 189 ++++----- .../Controllers/PublicClientController.php | 49 +-- app/Http/routes.php | 4 + app/Models/AccountGatewayToken.php | 14 + app/Models/Client.php | 7 +- app/Models/Payment.php | 16 +- app/Models/PaymentMethod.php | 157 ++++++++ app/Services/PaymentService.php | 372 ++++++++++-------- .../2016_05_10_144219_wepay_integration.php | 83 ++++ resources/lang/en/texts.php | 7 +- .../views/accounts/account_gateway.blade.php | 21 +- .../payments/paymentmethods_list.blade.php | 33 +- 12 files changed, 627 insertions(+), 325 deletions(-) create mode 100644 app/Models/PaymentMethod.php create mode 100644 database/migrations/2016_05_10_144219_wepay_integration.php diff --git a/app/Http/Controllers/PaymentController.php b/app/Http/Controllers/PaymentController.php index 8618fafdba58..491bac77033a 100644 --- a/app/Http/Controllers/PaymentController.php +++ b/app/Http/Controllers/PaymentController.php @@ -20,6 +20,7 @@ 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; @@ -406,6 +407,7 @@ class PaymentController extends BaseController $account = $client->account; $paymentType = Session::get($invitation->id . 'payment_type'); $accountGateway = $account->getGatewayByType($paymentType); + $paymentMethod = null; $rules = [ 'first_name' => 'required', @@ -435,6 +437,17 @@ class PaymentController extends BaseController 'country_id' => 'required', ]); } + + 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; + } + } if ($onSite) { $validator = Validator::make(Input::all(), $rules); @@ -476,11 +489,9 @@ class PaymentController extends BaseController } if ($useToken) { - $details['customerReference'] = $client->getGatewayToken(); + $details['customerReference'] = $customerReference; unset($details['token']); - if ($sourceId) { - $details['cardReference'] = $sourceId; - } + $details['cardReference'] = $sourceReference; } elseif ($account->token_billing_type_id == TOKEN_BILLING_ALWAYS || Input::get('token_billing') || $paymentType == PAYMENT_TYPE_STRIPE_ACH) { $token = $this->paymentService->createToken($gateway, $details, $accountGateway, $client, $invitation->contact_id, $customerReference/* return parameter */); if ($token) { @@ -509,13 +520,8 @@ class PaymentController extends BaseController } if ($useToken) { - $details['customerId'] = $customerId = $client->getGatewayToken(); - if (!$sourceId) { - $customer = $gateway->findCustomer($customerId)->send(); - $details['paymentMethodToken'] = $customer->getData()->paymentMethods[0]->token; - } else { - $details['paymentMethodToken'] = $sourceId; - } + $details['customerId'] = $customerReference; + $details['paymentMethodToken'] = $sourceReference; unset($details['token']); } elseif ($account->token_billing_type_id == TOKEN_BILLING_ALWAYS || Input::get('token_billing')) { $token = $this->paymentService->createToken($gateway, $details, $accountGateway, $client, $invitation->contact_id, $customerReference/* return parameter */); @@ -536,7 +542,6 @@ class PaymentController extends BaseController $response = $gateway->purchase($details)->send(); - if ($accountGateway->gateway_id == GATEWAY_EWAY) { $ref = $response->getData()['AccessCode']; } elseif ($accountGateway->gateway_id == GATEWAY_TWO_CHECKOUT) { @@ -563,7 +568,7 @@ class PaymentController extends BaseController } if ($response->isSuccessful()) { - $payment = $this->paymentService->createPayment($invitation, $accountGateway, $ref, null, $details, $response); + $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) { @@ -660,7 +665,7 @@ class PaymentController extends BaseController if ($response->isCancelled()) { // do nothing } elseif ($response->isSuccessful()) { - $payment = $this->paymentService->createPayment($invitation, $accountGateway, $ref, $payerId, $details, $purchaseResponse); + $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); @@ -738,7 +743,7 @@ class PaymentController extends BaseController ], 400); } - $data = static::getBankData($routingNumber); + $data = PaymentMethod::lookupBankData($routingNumber); if (is_string($data)) { return response()->json([ @@ -753,75 +758,10 @@ class PaymentController extends BaseController ], 404); } - public static function getBankData($routingNumber) { - $cached = Cache::get('bankData:'.$routingNumber); - - if ($cached != null) { - return $cached == false ? null : $cached; - } - - $dataPath = base_path('vendor/gatepay/FedACHdir/FedACHdir.txt'); - - if (!file_exists($dataPath) || !$size = filesize($dataPath)) { - return 'Invalid data file'; - } - - $lineSize = 157; - $numLines = $size/$lineSize; - - if ($numLines % 1 != 0) { - // The number of lines should be an integer - return 'Invalid data file'; - } - - // Format: http://www.sco.ca.gov/Files-21C/Bank_Master_Interface_Information_Package.pdf - $file = fopen($dataPath, 'r'); - - // Binary search - $low = 0; - $high = $numLines - 1; - while ($low <= $high) { - $mid = floor(($low + $high) / 2); - - fseek($file, $mid * $lineSize); - $thisNumber = fread($file, 9); - - if ($thisNumber > $routingNumber) { - $high = $mid - 1; - } else if ($thisNumber < $routingNumber) { - $low = $mid + 1; - } else { - $data = array('routing_number' => $thisNumber); - fseek($file, 26, SEEK_CUR); - $data['name'] = trim(fread($file, 36)); - $data['address'] = trim(fread($file, 36)); - $data['city'] = trim(fread($file, 20)); - $data['state'] = fread($file, 2); - $data['zip'] = fread($file, 5).'-'.fread($file, 4); - $data['phone'] = fread($file, 10); - break; - } - } - - if (!empty($data)) { - Cache::put('bankData:'.$routingNumber, $data, 5); - return $data; - } else { - Cache::put('bankData:'.$routingNumber, false, 5); - return null; - } - } - public function handlePaymentWebhook($accountKey, $gatewayId) { $gatewayId = intval($gatewayId); - if ($gatewayId != GATEWAY_STRIPE) { - return response()->json([ - 'message' => 'Unsupported gateway', - ], 404); - } - $account = Account::where('accounts.account_key', '=', $accountKey)->first(); if (!$account) { @@ -830,27 +770,48 @@ class PaymentController extends BaseController ], 404); } + $accountGateway = $account->getGatewayConfig(intval($gatewayId)); + + if (!$accountGateway) { + return response()->json([ + 'message' => 'Unknown gateway', + ], 404); + } + + switch($gatewayId) { + case GATEWAY_STRIPE: + return $this->handleStripeWebhook($accountGateway); + default: + return response()->json([ + 'message' => 'Unsupported gateway', + ], 404); + } + } + + 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); + return response()->json(['message' => 'Missing event id'], 400); } if (!$eventType) { - return response()->json([ - 'message' => 'Missing event type', - ], 400); + return response()->json(['message' => 'Missing event type'], 400); } - if (!in_array($eventType, array('charge.failed', 'charge.succeeded'))) { + $supportedEvents = array( + 'charge.failed', + 'charge.succeeded', + 'customer.source.updated', + 'customer.source.deleted', + ); + + if (!in_array($eventType, $supportedEvents)) { return array('message' => 'Ignoring event'); } - $accountGateway = $account->getGatewayConfig(intval($gatewayId)); - // Fetch the event directly from Stripe for security $eventDetails = $this->paymentService->makeStripeCall($accountGateway, 'GET', 'events/'.$eventId); @@ -861,33 +822,47 @@ class PaymentController extends BaseController } if ($eventType != $eventDetails['type']) { - return response()->json([ - 'message' => 'Event type mismatch', - ], 400); + return response()->json(['message' => 'Event type mismatch'], 400); } if (!$eventDetails['pending_webhooks']) { - return response()->json([ - 'message' => 'This is not a pending event', - ], 400); + return response()->json(['message' => 'This is not a pending event'], 400); } - $charge = $eventDetails['data']['object']; - $transactionRef = $charge['id']; - $payment = Payment::where('transaction_reference', '=', $transactionRef)->first(); + if ($eventType == 'charge.failed' || $eventType == 'charge.succeeded') { + $charge = $eventDetails['data']['object']; + $transactionRef = $charge['id']; - if (!$payment) { - return array('message' => 'Unknown payment'); - } + $payment = Payment::scope(false, $accountId)->where('transaction_reference', '=', $transactionRef)->first(); - if ($eventType == 'charge.failed') { - if (!$payment->isFailed()) { - $payment->markFailed($charge['failure_message']); - $this->userMailer->sendNotification($payment->user, $payment->invoice, 'payment_failed', $payment); + 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 == '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(); } - } elseif ($eventType == 'charge.succeeded') { - $payment->markComplete(); } return array('message' => 'Processed successfully'); diff --git a/app/Http/Controllers/PublicClientController.php b/app/Http/Controllers/PublicClientController.php index c5b22ee16204..b0bc58a45165 100644 --- a/app/Http/Controllers/PublicClientController.php +++ b/app/Http/Controllers/PublicClientController.php @@ -16,6 +16,7 @@ use Redirect; use App\Models\Gateway; use App\Models\Invitation; use App\Models\Document; +use App\ModelsPaymentMethod; use App\Ninja\Repositories\InvoiceRepository; use App\Ninja\Repositories\PaymentRepository; use App\Ninja\Repositories\ActivityRepository; @@ -170,28 +171,30 @@ class PublicClientController extends BaseController if ($paymentMethods) { foreach ($paymentMethods as $paymentMethod) { - if ($paymentMethod['type']->id != PAYMENT_TYPE_ACH || $paymentMethod['status'] == 'verified') { + if ($paymentMethod->payment_type_id != PAYMENT_TYPE_ACH || $paymentMethod->status == PAYMENT_METHOD_STATUS_VERIFIED) { + $code = htmlentities(str_replace(' ', '', strtolower($paymentMethod->payment_type->name))); - if ($paymentMethod['type']->id == PAYMENT_TYPE_ACH) { - $html = '
'.htmlentities($paymentMethod['bank_name']).'
'; - } elseif ($paymentMethod['type']->id == PAYMENT_TYPE_ID_PAYPAL) { + if ($paymentMethod->payment_type_id == PAYMENT_TYPE_ACH) { + if($paymentMethod->bank_data) { + $html = '
' . htmlentities($paymentMethod->bank_data->name) . '
'; + } + } elseif ($paymentMethod->payment_type_id == PAYMENT_TYPE_ID_PAYPAL) { $html = ''.trans('; } else { - $code = htmlentities(str_replace(' ', '', strtolower($paymentMethod['type']->name))); $html = ''.trans('; } - $url = URL::to("/payment/{$invitation->invitation_key}/token/".$paymentMethod['id']); + $url = URL::to("/payment/{$invitation->invitation_key}/token/".$paymentMethod->public_id); - if ($paymentMethod['type']->id == PAYMENT_TYPE_ID_PAYPAL) { - $html .= '  '.$paymentMethod['email'].''; + if ($paymentMethod->payment_type_id == PAYMENT_TYPE_ID_PAYPAL) { + $html .= '  '.$paymentMethod->email.''; $url .= '#braintree_paypal'; - } elseif ($paymentMethod['type']->id != PAYMENT_TYPE_ACH) { - $html .= '
'.trans('texts.card_expiration', array('expires' => Utils::fromSqlDate($paymentMethod['expiration'], false)->format('m/y'))).'
'; - $html .= '•••'.$paymentMethod['last4'].'
'; + } elseif ($paymentMethod->payment_type_id != PAYMENT_TYPE_ACH) { + $html .= '
'.trans('texts.card_expiration', array('expires' => Utils::fromSqlDate($paymentMethod->expiration, false)->format('m/y'))).'
'; + $html .= '•••'.$paymentMethod->last4.'
'; } else { $html .= '
'; - $html .= '•••'.$paymentMethod['last4'].'
'; + $html .= '•••'.$paymentMethod->last4.''; } $paymentTypes[] = [ @@ -452,9 +455,9 @@ class PublicClientController extends BaseController return $model->email; } } elseif ($model->last4) { - $bankData = PaymentController::getBankData($model->routing_number); - if (is_array($bankData)) { - return $bankData['name'].'  •••' . $model->last4; + $bankData = PaymentMethod::lookupBankData($model->routing_number); + if (is_object($bankData)) { + return $bankData->name.'  •••' . $model->last4; } elseif($model->last4) { return '' . htmlentities($card_type) . '  •••' . $model->last4; } @@ -770,7 +773,7 @@ class PublicClientController extends BaseController public function verifyPaymentMethod() { - $sourceId = Input::get('source_id'); + $publicId = Input::get('source_id'); $amount1 = Input::get('verification1'); $amount2 = Input::get('verification2'); @@ -779,7 +782,7 @@ class PublicClientController extends BaseController } $client = $invitation->invoice->client; - $result = $this->paymentService->verifyClientPaymentMethod($client, $sourceId, $amount1, $amount2); + $result = $this->paymentService->verifyClientPaymentMethod($client, $publicId, $amount1, $amount2); if (is_string($result)) { Session::flash('error', $result); @@ -790,14 +793,14 @@ class PublicClientController extends BaseController return redirect()->to($client->account->enable_client_portal?'/client/dashboard':'/client/paymentmethods/'); } - public function removePaymentMethod($sourceId) + public function removePaymentMethod($publicId) { if (!$invitation = $this->getInvitation()) { return $this->returnError(); } $client = $invitation->invoice->client; - $result = $this->paymentService->removeClientPaymentMethod($client, $sourceId); + $result = $this->paymentService->removeClientPaymentMethod($client, $publicId); if (is_string($result)) { Session::flash('error', $result); @@ -824,9 +827,9 @@ class PublicClientController extends BaseController $gateway = $accountGateway->gateway; if ($token && $paymentType == PAYMENT_TYPE_BRAINTREE_PAYPAL) { - $sourceId = $this->paymentService->createToken($this->paymentService->createGateway($accountGateway), array('token'=>$token), $accountGateway, $client, $invitation->contact_id); + $sourceReference = $this->paymentService->createToken($this->paymentService->createGateway($accountGateway), array('token'=>$token), $accountGateway, $client, $invitation->contact_id); - if(empty($sourceId)) { + if(empty($sourceReference)) { $this->paymentMethodError('Token-No-Ref', $this->paymentService->lastError, $accountGateway); } else { Session::flash('message', trans('texts.payment_method_added')); @@ -895,12 +898,12 @@ class PublicClientController extends BaseController if (!empty($details)) { $gateway = $this->paymentService->createGateway($accountGateway); - $sourceId = $this->paymentService->createToken($gateway, $details, $accountGateway, $client, $invitation->contact_id); + $sourceReference = $this->paymentService->createToken($gateway, $details, $accountGateway, $client, $invitation->contact_id); } else { return Redirect::to('client/paymentmethods/add/' . $typeLink)->withInput(Request::except('cvv')); } - if(empty($sourceId)) { + if(empty($sourceReference)) { $this->paymentMethodError('Token-No-Ref', $this->paymentService->lastError, $accountGateway); return Redirect::to('client/paymentmethods/add/' . $typeLink)->withInput(Request::except('cvv')); } else if ($paymentType == PAYMENT_TYPE_STRIPE_ACH && empty($usingPlaid) ) { diff --git a/app/Http/routes.php b/app/Http/routes.php index 241049810960..e8d7f9bbeac3 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -647,6 +647,10 @@ if (!defined('CONTACT_EMAIL')) { define('PAYMENT_TYPE_SOLO', 22); define('PAYMENT_TYPE_SWITCH', 23); + define('PAYMENT_METHOD_STATUS_NEW', 'new'); + 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'); diff --git a/app/Models/AccountGatewayToken.php b/app/Models/AccountGatewayToken.php index b706882af77d..06494325662e 100644 --- a/app/Models/AccountGatewayToken.php +++ b/app/Models/AccountGatewayToken.php @@ -8,4 +8,18 @@ class AccountGatewayToken extends Eloquent use SoftDeletes; protected $dates = ['deleted_at']; public $timestamps = true; + + protected $casts = [ + 'uses_local_payment_methods' => 'boolean', + ]; + + public function payment_methods() + { + return $this->hasMany('App\Models\PaymentMethod'); + } + + public function default_payment_method() + { + return $this->hasOne('App\Models\PaymentMethod', 'id', 'default_payment_method_id'); + } } \ No newline at end of file diff --git a/app/Models/Client.php b/app/Models/Client.php index bc6e94e649fc..0198d46c200d 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -261,7 +261,7 @@ class Client extends EntityModel } - public function getGatewayToken(&$accountGateway) + public function getGatewayToken(&$accountGateway = null, &$token = null) { $account = $this->account; @@ -272,7 +272,10 @@ class Client extends EntityModel if (!count($account->account_gateways)) { return false; } - $accountGateway = $account->getTokenGateway(); + + if (!$accountGateway){ + $accountGateway = $account->getTokenGateway(); + } if (!$accountGateway) { return false; diff --git a/app/Models/Payment.php b/app/Models/Payment.php index faa42e3c93f3..b0001569dcc8 100644 --- a/app/Models/Payment.php +++ b/app/Models/Payment.php @@ -8,6 +8,7 @@ use App\Events\PaymentWasVoided; use App\Events\PaymentCompleted; use App\Events\PaymentVoided; use App\Events\PaymentFailed; +use App\Models\PaymentMethod; use Laracasts\Presenter\PresentableTrait; class Payment extends EntityModel @@ -56,7 +57,12 @@ class Payment extends EntityModel public function payment_type() { return $this->belongsTo('App\Models\PaymentType'); - } + } + + public function payment_method() + { + return $this->belongsTo('App\Models\PaymentMethod'); + } public function payment_status() { @@ -160,6 +166,14 @@ class Payment extends EntityModel { return ENTITY_PAYMENT; } + + public function getBankData() + { + if (!$this->routing_number) { + return null; + } + return PaymentMethod::lookupBankData($this->routing_number); + } } Payment::creating(function ($payment) { diff --git a/app/Models/PaymentMethod.php b/app/Models/PaymentMethod.php new file mode 100644 index 000000000000..62072dca090f --- /dev/null +++ b/app/Models/PaymentMethod.php @@ -0,0 +1,157 @@ +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'); + } + + public function contact() + { + return $this->belongsTo('App\Models\Contact'); + } + + public function account_gateway_token() + { + return $this->belongsTo('App\Models\AccountGatewayToken'); + } + + public function payment_type() + { + return $this->belongsTo('App\Models\PaymentType'); + } + + public function currency() + { + return $this->belongsTo('App\Models\Currency'); + } + + public function payments() + { + return $this->hasMany('App\Models\Payments'); + } + + public function getBankData() + { + if (!$this->routing_number) { + return null; + } + return static::lookupBankData($this->routing_number); + } + + public function scopeScope($query, $publicId = false, $accountId = false, $accountGatewayTokenId = false) + { + $query = parent::scopeScope($query, $publicId, $accountId); + + if ($accountGatewayTokenId) { + $query->where($this->getTable() . '.account_gateway_token_id', '=', $accountGatewayTokenId); + } + + return $query; + } + + public static function lookupBankData($routingNumber) { + $cached = Cache::get('bankData:'.$routingNumber); + + if ($cached != null) { + return $cached == false ? null : $cached; + } + + $dataPath = base_path('vendor/gatepay/FedACHdir/FedACHdir.txt'); + + if (!file_exists($dataPath) || !$size = filesize($dataPath)) { + return 'Invalid data file'; + } + + $lineSize = 157; + $numLines = $size/$lineSize; + + if ($numLines % 1 != 0) { + // The number of lines should be an integer + return 'Invalid data file'; + } + + // Format: http://www.sco.ca.gov/Files-21C/Bank_Master_Interface_Information_Package.pdf + $file = fopen($dataPath, 'r'); + + // Binary search + $low = 0; + $high = $numLines - 1; + while ($low <= $high) { + $mid = floor(($low + $high) / 2); + + fseek($file, $mid * $lineSize); + $thisNumber = fread($file, 9); + + if ($thisNumber > $routingNumber) { + $high = $mid - 1; + } else if ($thisNumber < $routingNumber) { + $low = $mid + 1; + } else { + $data = new \stdClass(); + $data->routing_number = $thisNumber; + + fseek($file, 26, SEEK_CUR); + + $data->name = trim(fread($file, 36)); + $data->address = trim(fread($file, 36)); + $data->city = trim(fread($file, 20)); + $data->state = fread($file, 2); + $data->zip = fread($file, 5).'-'.fread($file, 4); + $data->phone = fread($file, 10); + break; + } + } + + if (!empty($data)) { + Cache::put('bankData:'.$routingNumber, $data, 5); + return $data; + } else { + Cache::put('bankData:'.$routingNumber, false, 5); + return null; + } + } +} + +PaymentMethod::deleting(function($paymentMethod) { + $accountGatewayToken = $paymentMethod->account_gateway_token; + if ($accountGatewayToken->default_payment_method_id == $paymentMethod->id) { + $newDefault = $accountGatewayToken->payment_methods->first(function($i, $paymentMethdod) use ($accountGatewayToken){ + return $paymentMethdod->id != $accountGatewayToken->default_payment_method_id; + }); + $accountGatewayToken->default_payment_method_id = $newDefault ? $newDefault->id : null; + $accountGatewayToken->save(); + } +}); \ No newline at end of file diff --git a/app/Services/PaymentService.php b/app/Services/PaymentService.php index 3f938141dd2b..101c79f92c34 100644 --- a/app/Services/PaymentService.php +++ b/app/Services/PaymentService.php @@ -10,6 +10,7 @@ use Omnipay; use Session; use CreditCard; use App\Models\Payment; +use App\Models\PaymentMethod; use App\Models\Account; use App\Models\Country; use App\Models\Client; @@ -26,7 +27,7 @@ class PaymentService extends BaseService { public $lastError; protected $datatableService; - + protected static $refundableGateways = array( GATEWAY_STRIPE, GATEWAY_BRAINTREE @@ -47,7 +48,7 @@ class PaymentService extends BaseService public function createGateway($accountGateway) { $gateway = Omnipay::create($accountGateway->gateway->provider); - $gateway->initialize((array) $accountGateway->getConfig()); + $gateway->initialize((array)$accountGateway->getConfig()); if ($accountGateway->isGateway(GATEWAY_DWOLLA)) { if ($gateway->getSandbox() && isset($_ENV['DWOLLA_SANDBOX_KEY']) && isset($_ENV['DWOLLA_SANSBOX_SECRET'])) { @@ -66,7 +67,7 @@ class PaymentService extends BaseService { $invoice = $invitation->invoice; $account = $invoice->account; - $key = $invoice->account_id.'-'.$invoice->invoice_number; + $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) { @@ -94,7 +95,7 @@ class PaymentService extends BaseService $data['ButtonSource'] = 'InvoiceNinja_SP'; }; - if($input && $accountGateway->isGateway(GATEWAY_STRIPE)) { + if ($input && $accountGateway->isGateway(GATEWAY_STRIPE)) { if (!empty($input['stripeToken'])) { $data['token'] = $input['stripeToken']; unset($details['card']); @@ -118,12 +119,12 @@ class PaymentService extends BaseService '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['country_id'])) { $country = Country::find($input['country_id']); @@ -174,198 +175,134 @@ class PaymentService extends BaseService ]; } - public function getClientPaymentMethods($client) { - $token = $client->getGatewayToken($accountGateway); + public function getClientPaymentMethods($client) + { + $token = $client->getGatewayToken($accountGateway/* return parameter */, $accountGatewayToken/* return parameter */); if (!$token) { return null; } - $gateway = $this->createGateway($accountGateway); - - $paymentMethods = array(); - if ($accountGateway->gateway_id == GATEWAY_STRIPE) { + 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(); - $default_source = $data['default_source']; $sources = isset($data['sources']) ? $data['sources']['data'] : $data['cards']['data']; - $paymentTypes = Cache::get('paymentTypes'); - $currencies = Cache::get('currencies'); + // Load + $accountGatewayToken->payment_methods(); foreach ($sources as $source) { - if ($source['object'] == 'bank_account') { - $paymentMethods[] = array( - 'id' => $source['id'], - 'default' => $source['id'] == $default_source, - 'type' => $paymentTypes->find(PAYMENT_TYPE_ACH), - 'currency' => $currencies->where('code', strtoupper($source['currency']))->first(), - 'last4' => $source['last4'], - 'routing_number' => $source['routing_number'], - 'bank_name' => $source['bank_name'], - 'status' => $source['status'], - ); - } elseif ($source['object'] == 'card') { - $paymentMethods[] = array( - 'id' => $source['id'], - 'default' => $source['id'] == $default_source, - 'type' => $paymentTypes->find($this->parseCardType($source['brand'])), - 'last4' => $source['last4'], - 'expiration' => $source['exp_year'] . '-' . $source['exp_month'] . '-00', - ); + $paymentMethod = $this->convertPaymentMethodFromStripe($source, $accountGatewayToken); + if ($paymentMethod) { + $paymentMethod->save(); + } + + if ($data['default_source'] == $source['id']) { + $accountGatewayToken->default_payment_method_id = $paymentMethod->id; } } - } elseif ($accountGateway->gateway_id == GATEWAY_BRAINTREE) { - $response = $gateway->findCustomer($token)->send(); - $data = $response->getData(); - if (!($data instanceof \Braintree\Customer)) { - return null; - } - - $sources = $data->paymentMethods; - - $paymentTypes = Cache::get('paymentTypes'); - $currencies = Cache::get('currencies'); - foreach ($sources as $source) { - if ($source instanceof \Braintree\CreditCard) { - $paymentMethods[] = array( - 'id' => $source->token, - 'default' => $source->isDefault(), - 'type' => $paymentTypes->find($this->parseCardType($source->cardType)), - 'last4' => $source->last4, - 'expiration' => $source->expirationYear . '-' . $source->expirationMonth . '-00', - ); - } elseif ($source instanceof \Braintree\PayPalAccount) { - $paymentMethods[] = array( - 'id' => $source->token, - 'default' => $source->isDefault(), - 'type' => $paymentTypes->find(PAYMENT_TYPE_ID_PAYPAL), - 'email' => $source->email, - ); - } - } + $accountGatewayToken->uses_local_payment_methods = true; + $accountGatewayToken->save(); } - return $paymentMethods; + return $accountGatewayToken->payment_methods; } - public function verifyClientPaymentMethod($client, $sourceId, $amount1, $amount2) { + public function verifyClientPaymentMethod($client, $publicId, $amount1, $amount2) + { $token = $client->getGatewayToken($accountGateway); 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 - return $this->makeStripeCall( + $result = $this->makeStripeCall( $accountGateway, 'POST', - 'customers/'.$token.'/sources/'.$sourceId.'/verify', - 'amounts[]='.intval($amount1).'&amounts[]='.intval($amount2) + 'customers/' . $token . '/sources/' . $paymentMethod->source_reference . '/verify', + 'amounts[]=' . intval($amount1) . '&amounts[]=' . intval($amount2) ); - } - public function removeClientPaymentMethod($client, $sourceId) { - $token = $client->getGatewayToken($accountGateway/* return parameter */); - if (!$token) { - return null; - } + if (!is_string($result)) { + $paymentMethod->status = PAYMENT_METHOD_STATUS_VERIFIED; + $paymentMethod->save(); - $gateway = $this->createGateway($accountGateway); - - if ($accountGateway->gateway_id == GATEWAY_STRIPE) { - $response = $gateway->deleteCard(array('customerReference' => $token, 'cardReference'=>$sourceId))->send(); - if (!$response->isSuccessful()) { - return $response->getMessage(); - } - } elseif ($accountGateway->gateway_id == GATEWAY_BRAINTREE) { - // Make sure the source is owned by this client - $sources = $this->getClientPaymentMethods($client); - $ownsSource = false; - foreach ($sources as $source) { - if ($source['id'] == $sourceId) { - $ownsSource = true; - break; - } - } - if (!$ownsSource) { - return 'Unknown source'; - } - - $response = $gateway->deletePaymentMethod(array('token'=>$sourceId))->send(); - - if (!$response->isSuccessful()) { - return $response->getMessage(); + 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 setClientDefaultPaymentMethod($client, $sourceId) { - $token = $client->getGatewayToken($accountGateway/* return parameter */); - if (!$token) { - return null; - } - - $gateway = $this->createGateway($accountGateway); - - if ($accountGateway->gateway_id == GATEWAY_STRIPE) { - - return $this->makeStripeCall( - $accountGateway, - 'POST', - 'customers/'.$token, - 'default_card='.$sourceId - ); - } elseif ($accountGateway->gateway_id == GATEWAY_BRAINTREE) { - // Make sure the source is owned by this client - $sources = $this->getClientPaymentMethods($client); - $ownsSource = false; - foreach ($sources as $source) { - if ($source['id'] == $sourceId) { - $ownsSource = true; - break; - } - } - if (!$ownsSource) { - return 'Unknown source'; - } - - $response = $gateway->updatePaymentMethod(array( - 'token' => $sourceId, - 'makeDefault' => true, - ))->send(); - - if (!$response->isSuccessful()) { - return $response->getMessage(); - } - } - - return true; - } - - public function createToken($gateway, $details, $accountGateway, $client, $contactId, &$customerReference = null) + public function removeClientPaymentMethod($client, $publicId) { - $customerReference = $client->getGatewayToken(); + $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(); + } + } + + $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($gateway, $details, $accountGateway, $client, $contactId, &$customerReference = null, &$paymentMethod = null) + { + $customerReference = $client->getGatewayToken($accountGateway, $accountGatewayToken/* return paramenter */); if ($customerReference) { $details['customerReference'] = $customerReference; if ($accountGateway->gateway->id == GATEWAY_STRIPE) { - $customerResponse = $gateway->fetchCustomer(array('customerReference'=>$customerReference))->send(); + $customerResponse = $gateway->fetchCustomer(array('customerReference' => $customerReference))->send(); - if (!$customerResponse->isSuccessful()){ + 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)){ + if (!($customer instanceof \Braintree\Customer)) { $customerReference = null; // The customer might not exist anymore } } @@ -413,7 +350,7 @@ class PaymentService extends BaseService } else { $data = $tokenResponse->getData(); if ($data && $data['error'] && $data['error']['type'] == 'invalid_request_error') { - $this->lastError = $data['error']['message']; + $this->lastError = $data['error']['message']; return; } } @@ -435,7 +372,7 @@ class PaymentService extends BaseService if ($customerReference) { $token = AccountGatewayToken::where('client_id', '=', $client->id) - ->where('account_gateway_id', '=', $accountGateway->id)->first(); + ->where('account_gateway_id', '=', $accountGateway->id)->first(); if (!$token) { $token = new AccountGatewayToken(); @@ -447,6 +384,9 @@ class PaymentService extends BaseService $token->token = $customerReference; $token->save(); + + $paymentMethod = $this->createPaymentMethodFromGatewayResponse($tokenResponse, $accountGateway, $accountGatewayToken, $contactId); + } else { $this->lastError = $tokenResponse->getMessage(); } @@ -454,6 +394,104 @@ 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'] . '-00'; + $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 . '-00'; + } 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 createPaymentMethodFromGatewayResponse($gatewayResponse, $accountGateway, $accountGatewayToken = null, $contactId = null) { + if ($accountGateway->gateway_id == GATEWAY_STRIPE) { + $data = $gatewayResponse->getData(); + if(!empty($data['object']) && ($data['object'] == 'card' || $data['object'] == 'bank_account')) { + $source = $data; + } else { + $source = !empty($data['source']) ? $data['source'] : $data['card']; + } + + if ($source) { + $paymentMethod = $this->convertPaymentMethodFromStripe($source, $accountGatewayToken); + } + } elseif ($accountGateway->gateway_id == GATEWAY_BRAINTREE) { + $paymentMethod = $accountGatewayToken ? PaymentMethod::createNew($accountGatewayToken) : new PaymentMethod(); + + $transaction = $sourceResponse->getData()->transaction; + if ($transaction->paymentInstrumentType == 'credit_card') { + $card = $transaction->creditCardDetails; + $paymentMethod->last4 = $card->last4; + $paymentMethod->expiration = $card->expirationYear . '-' . $card->expirationMonth . '-00'; + $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)); + } + + if (!empty($paymentMethod) && $accountGatewayToken && $contactId) { + $paymentMethod->account_gateway_token_id = $accountGatewayToken->id; + $paymentMethod->account_id = $accountGatewayToken->account_id; + $paymentMethod->contact_id = $contactId; + $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 $paymentMethod; + } + public function getCheckoutComToken($invitation) { $token = false; @@ -490,7 +528,7 @@ class PaymentService extends BaseService return $token; } - public function createPayment($invitation, $accountGateway, $ref, $payerId = null, $paymentDetails = null, $purchaseResponse = null) + public function createPayment($invitation, $accountGateway, $ref, $payerId = null, $paymentDetails = null, $paymentMethod = null, $purchaseResponse = null) { $invoice = $invitation->invoice; @@ -551,6 +589,10 @@ class PaymentService extends BaseService $payment->payer_id = $payerId; } + if ($paymentMethod) { + $payment->payment_method_id = $paymentMethod->id; + } + $payment->save(); // enable pro plan for hosted users @@ -665,28 +707,28 @@ class PaymentService extends BaseService public function autoBillInvoice($invoice) { $client = $invoice->client; - $account = $invoice->account; - $invitation = $invoice->invitations->first(); - $accountGateway = $account->getTokenGateway(); - $token = $client->getGatewayToken(); - if (!$invitation || !$accountGateway || !$token) { + // Make sure we've migrated in data from Stripe + $this->getClientPaymentMethods($client); + + $invitation = $invoice->invitations->first(); + $token = $client->getGatewayToken($accountGateway/* return parameter */, $accountGatewayToken/* return parameter */); + $defaultPaymentMethod = $accountGatewayToken->default_payment_method; + + if (!$invitation || !$token || !$defaultPaymentMethod) { return false; } // setup the gateway/payment info $gateway = $this->createGateway($accountGateway); $details = $this->getPaymentDetails($invitation, $accountGateway); - $details['customerReference'] = $token; if ($accountGateway->gateway_id == GATEWAY_STRIPE) { $details['customerReference'] = $token; - + $details['cardReference'] = $defaultPaymentMethod->sourceReference; } elseif ($accountGateway->gateway_id == GATEWAY_BRAINTREE) { $details['customerId'] = $token; - $customer = $gateway->findCustomer($token)->send()->getData(); - $defaultPaymentMethod = $customer->defaultPaymentMethod(); - $details['paymentMethodToken'] = $defaultPaymentMethod->token; + $details['paymentMethodToken'] = $defaultPaymentMethod->sourceReference; } // submit purchase/get response @@ -694,7 +736,7 @@ class PaymentService extends BaseService if ($response->isSuccessful()) { $ref = $response->getTransactionReference(); - return $this->createPayment($invitation, $accountGateway, $ref, null, $details, $response); + return $this->createPayment($invitation, $accountGateway, $ref, null, $details, $defaultPaymentMethod, $response); } else { return false; } @@ -761,9 +803,9 @@ class PaymentService extends BaseService return $model->email; } } elseif ($model->last4) { - $bankData = PaymentController::getBankData($model->routing_number); - if (is_array($bankData)) { - return $bankData['name'].'  •••' . $model->last4; + $bankData = PaymentMethod::lookupBankData($model->routing_number); + if (is_object($bankData)) { + return $bankData->name.'  •••' . $model->last4; } elseif($model->last4) { return '' . htmlentities($card_type) . '  •••' . $model->last4; } diff --git a/database/migrations/2016_05_10_144219_wepay_integration.php b/database/migrations/2016_05_10_144219_wepay_integration.php new file mode 100644 index 000000000000..c2a48afd8098 --- /dev/null +++ b/database/migrations/2016_05_10_144219_wepay_integration.php @@ -0,0 +1,83 @@ +increments('id'); + $table->unsignedInteger('account_id'); + $table->unsignedInteger('contact_id')->nullable(); + $table->unsignedInteger('account_gateway_token_id'); + $table->unsignedInteger('payment_type_id'); + $table->string('source_reference'); + + $table->unsignedInteger('routing_number')->nullable(); + $table->smallInteger('last4')->unsigned()->nullable(); + $table->date('expiration')->nullable(); + $table->string('email')->nullable(); + $table->unsignedInteger('currency_id')->nullable(); + $table->string('status')->nullable(); + + $table->timestamps(); + $table->softDeletes(); + + $table->foreign('account_id')->references('id')->on('accounts')->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'); + $table->foreign('currency_id')->references('id')->on('currencies'); + + $table->unsignedInteger('public_id')->index(); + $table->unique( array('account_id','public_id') ); + }); + + Schema::table('payments', function($table) + { + $table->unsignedInteger('payment_method_id')->nullable(); + $table->foreign('payment_method_id')->references('id')->on('payment_methods'); + }); + + Schema::table('account_gateway_tokens', function($table) + { + $table->unsignedInteger('default_payment_method_id')->nullable(); + $table->foreign('default_payment_method_id')->references('id')->on('payment_methods'); + + $table->boolean('uses_local_payment_methods')->defalut(true); + }); + + \DB::table('account_gateway_tokens')->update(array('uses_local_payment_methods' => false)); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('payments', function($table) + { + $table->dropForeign('payments_payment_method_id_foreign'); + $table->dropColumn('payment_method_id'); + }); + + Schema::table('account_gateway_tokens', function($table) + { + $table->dropForeign('account_gateway_tokens_default_payment_method_id_foreign'); + $table->dropColumn('default_payment_method_id'); + $table->dropColumn('uses_local_payment_methods'); + }); + + Schema::dropIfExists('payment_methods'); + } +} diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index 1a7d6a1267bb..32a31dca7d58 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -1259,7 +1259,7 @@ $LANG = array( 'payment_method_set_as_default' => 'Set Autobill payment method.', 'activity_41' => ':payment_amount payment (:payment) failed', 'webhook_url' => 'Webhook URL', - 'stripe_webhook_help' => 'You must :link for ACH payment status to be updated.', + 'stripe_webhook_help' => 'You must :link.', 'stripe_webhook_help_link_text' => 'add this URL as an endpoint at Stripe', 'payment_method_error' => 'There was an error adding your payment methd. Please try again later.', 'notification_invoice_payment_failed_subject' => 'Payment failed for Invoice :invoice', @@ -1286,7 +1286,10 @@ $LANG = array( 'braintree_paypal_help' => 'You must also :link.', 'braintree_paypal_help_link_text' => 'link PayPal to your BrainTree account', 'token_billing_braintree_paypal' => 'Save payment details', - 'add_paypal_account' => 'Add PayPal Account' + 'add_paypal_account' => 'Add PayPal Account', + + + 'no_payment_method_specified' => 'No payment method specified', ); return $LANG; diff --git a/resources/views/accounts/account_gateway.blade.php b/resources/views/accounts/account_gateway.blade.php index bd5f100671a6..ade161f5b65f 100644 --- a/resources/views/accounts/account_gateway.blade.php +++ b/resources/views/accounts/account_gateway.blade.php @@ -97,6 +97,18 @@ ->help(trans('texts.token_billing_help')) !!} @endif + @if ($gateway->id == GATEWAY_STRIPE) +
+ +
+ +
{!! trans('texts.stripe_webhook_help', [ + 'link'=>''.trans('texts.stripe_webhook_help_link_text').'' + ]) !!}
+
+
+ @endif + @if ($gateway->id == GATEWAY_BRAINTREE) @if ($account->getGatewayByType(PAYMENT_TYPE_PAYPAL)) {!! Former::checkbox('enable_paypal') @@ -148,15 +160,6 @@ ->text(trans('texts.enable_ach')) ->help(trans('texts.stripe_ach_help')) !!}
-
- -
- -
{!! trans('texts.stripe_webhook_help', [ - 'link'=>''.trans('texts.stripe_webhook_help_link_text').'' - ]) !!}
-
-

{{trans('texts.plaid')}}

diff --git a/resources/views/payments/paymentmethods_list.blade.php b/resources/views/payments/paymentmethods_list.blade.php index 3436315acf6a..1522f2ac0a94 100644 --- a/resources/views/payments/paymentmethods_list.blade.php +++ b/resources/views/payments/paymentmethods_list.blade.php @@ -53,30 +53,31 @@ @foreach ($paymentMethods as $paymentMethod)
- {{trans(name)))}}"> + {{trans(payment_type->name)))}}"> - @if(!empty($paymentMethod['last4'])) - •••••{{$paymentMethod['last4']}} + @if(!empty($paymentMethod->last4)) + •••••{{$paymentMethod->last4}} @endif - - @if($paymentMethod['type']->id == PAYMENT_TYPE_ACH) - {{ $paymentMethod['bank_name'] }} - @if($paymentMethod['status'] == 'new') - ({{trans('texts.complete_verification')}}) - @elseif($paymentMethod['status'] == 'verification_failed') + @if($paymentMethod->payment_type_id == PAYMENT_TYPE_ACH) + @if($paymentMethod->bank()) + {{ $paymentMethod->bank()->name }} + @endif + @if($paymentMethod->status == PAYMENT_METHOD_STATUS_NEW) + ({{trans('texts.complete_verification')}}) + @elseif($paymentMethod->status == PAYMENT_METHOD_STATUS_VERIFICATION_FAILED) ({{trans('texts.verification_failed')}}) @endif - @elseif($paymentMethod['type']->id == PAYMENT_TYPE_ID_PAYPAL) - {{ $paymentMethod['email'] }} + @elseif($paymentMethod->type_id == PAYMENT_TYPE_ID_PAYPAL) + {{ $paymentMethod->email }} @else - {!! trans('texts.card_expiration', array('expires'=>Utils::fromSqlDate($paymentMethod['expiration'], false)->format('m/y'))) !!} + {!! trans('texts.card_expiration', array('expires'=>Utils::fromSqlDate($paymentMethod->expiration, false)->format('m/y'))) !!} @endif - @if($paymentMethod['default']) + @if($paymentMethod->id == $paymentMethod->account_gateway_token->default_payment_method_id) ({{trans('texts.used_for_auto_bill')}}) - @elseif($paymentMethod['type']->id != PAYMENT_TYPE_ACH || $paymentMethod['status'] == 'verified') - ({{trans('texts.use_for_auto_bill')}}) + @elseif($paymentMethod->payment_type_id != PAYMENT_TYPE_ACH || $paymentMethod->status == PAYMENT_METHOD_STATUS_VERIFIED) + ({{trans('texts.use_for_auto_bill')}}) @endif - × + ×
@endforeach @endif