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 = '
';
} else {
- $code = htmlentities(str_replace(' ', '', strtolower($paymentMethod['type']->name)));
$html = '
';
}
- $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 '
•••' . $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 '
•••' . $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)
+
+ @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')) !!}