diff --git a/app/Http/Controllers/PaymentController.php b/app/Http/Controllers/PaymentController.php
index f5ecc89f8e8e..b23f0cb7b7ab 100644
--- a/app/Http/Controllers/PaymentController.php
+++ b/app/Http/Controllers/PaymentController.php
@@ -55,6 +55,7 @@ class PaymentController extends BaseController
'client',
'transaction_reference',
'method',
+ 'source',
'payment_amount',
'payment_date',
'status',
@@ -439,10 +440,12 @@ class PaymentController extends BaseController
if ($useToken) {
$details['customerReference'] = $client->getGatewayToken();
+ 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);
+ $token = $this->paymentService->createToken($gateway, $details, $accountGateway, $client, $invitation->contact_id, $customerReference);
if ($token) {
- $details['customerReference'] = $token;
+ $details['token'] = $token;
+ $details['customerReference'] = $customerReference;
} else {
$this->error('Token-No-Ref', $this->paymentService->lastError, $accountGateway);
return Redirect::to('payment/'.$invitationKey)->withInput(Request::except('cvv'));
@@ -479,7 +482,49 @@ class PaymentController extends BaseController
}
if ($response->isSuccessful()) {
- $payment = $this->paymentService->createPayment($invitation, $accountGateway, $ref);
+ $last4 = null;
+ $expiration = null;
+ $card_type_id = null;
+
+ if (!empty($details['card'])) {
+ $card = $details['card'];
+ $last4 = substr($card->number, -4);
+ $year = $card->expiryYear;
+ if (strlen($year) == 2) {
+ $year = '20' . $year;
+ }
+
+ $expiration = $year . '-' . $card->expiryMonth . '-00';
+ $card_type_id = $this->detectCardType($card->number);
+ }
+
+ if ($accountGateway->gateway_id == GATEWAY_STRIPE) {
+ $card = $response->getSource();
+ if (!$card) {
+ $card = $response->getCard();
+ }
+
+ if ($card) {
+ $last4 = $card['last4'];
+ $expiration = $card['exp_year'] . '-' . $card['exp_month'] . '-00';
+ $stripe_card_types = array(
+ 'Visa' => CARD_VISA,
+ 'American Express' => CARD_AMERICAN_EXPRESS,
+ 'MasterCard' => CARD_MASTERCARD,
+ 'Discover' => CARD_DISCOVER,
+ 'JCB' => CARD_JCB,
+ 'Diners Club' => CARD_DINERS_CLUB
+ );
+
+ if (!empty($stripe_card_types[$card['brand']])) {
+ $card_type_id = $stripe_card_types[$card['brand']];
+ } else {
+ $card_type_id = CARD_UNKNOWN;
+ }
+ }
+ }
+
+ $payment = $this->paymentService->createPayment($invitation, $accountGateway, $ref, null, $last4, $expiration, $card_type_id);
Session::flash('message', trans('texts.applied_payment'));
if ($account->account_key == NINJA_ACCOUNT_KEY) {
@@ -512,6 +557,24 @@ class PaymentController extends BaseController
}
}
}
+
+ private function detectCardType($number)
+ {
+ if (preg_match('/^3[47][0-9]{13}$/',$number)) {
+ return CARD_AMERICAN_EXPRESS;
+ } elseif (preg_match('/^3(?:0[0-5]|[68][0-9])[0-9]{11}$/',$number)) {
+ return CARD_DINERS_CLUB;
+ } elseif (preg_match('/^6(?:011|5[0-9][0-9])[0-9]{12}$/',$number)) {
+ return CARD_DISCOVER;
+ } elseif (preg_match('/^(?:2131|1800|35\d{3})\d{11}$/',$number)) {
+ return CARD_JCB;
+ } elseif (preg_match('/^5[1-5][0-9]{14}$/',$number)) {
+ return CARD_MASTERCARD;
+ } elseif (preg_match('/^4[0-9]{12}(?:[0-9]{3})?$/',$number)) {
+ return CARD_VISA;
+ }
+ return CARD_UNKNOWN;
+ }
public function offsite_payment()
{
diff --git a/app/Http/Controllers/PublicClientController.php b/app/Http/Controllers/PublicClientController.php
index d756bd7eaaa0..085098827e1b 100644
--- a/app/Http/Controllers/PublicClientController.php
+++ b/app/Http/Controllers/PublicClientController.php
@@ -324,7 +324,7 @@ class PublicClientController extends BaseController
'clientFontUrl' => $account->getFontsUrl(),
'entityType' => ENTITY_PAYMENT,
'title' => trans('texts.payments'),
- 'columns' => Utils::trans(['invoice', 'transaction_reference', 'method', 'payment_amount', 'payment_date', 'status'])
+ 'columns' => Utils::trans(['invoice', 'transaction_reference', 'method', 'source', 'payment_amount', 'payment_date', 'status'])
];
return response()->view('public_list', $data);
@@ -341,9 +341,16 @@ class PublicClientController extends BaseController
->addColumn('invoice_number', function ($model) { return $model->invitation_key ? link_to('/view/'.$model->invitation_key, $model->invoice_number)->toHtml() : $model->invoice_number; })
->addColumn('transaction_reference', function ($model) { return $model->transaction_reference ? $model->transaction_reference : 'Manual entry'; })
->addColumn('payment_type', function ($model) { return $model->payment_type ? $model->payment_type : ($model->account_gateway_id ? 'Online payment' : ''); })
+ ->addColumn('payment_source', function ($model) {
+ if (!$model->card_type_code) return '';
+ $card_type = trans("texts.card_" . $model->card_type_code);
+ $expiration = trans('texts.card_expiration', array('expires'=>Utils::fromSqlDate($model->expiration, false)->format('m/d')));
+ return '
•••'.$model->last4.' '.$expiration;
+ })
->addColumn('amount', function ($model) { return Utils::formatMoney($model->amount, $model->currency_id, $model->country_id); })
->addColumn('payment_date', function ($model) { return Utils::dateToString($model->payment_date); })
->addColumn('status', function ($model) { return $this->getPaymentStatusLabel($model); })
+ ->orderColumns( 'invoice_number', 'transaction_reference', 'payment_type', 'amount', 'payment_date')
->make();
}
diff --git a/app/Http/routes.php b/app/Http/routes.php
index 25041048f102..9903b3caee0f 100644
--- a/app/Http/routes.php
+++ b/app/Http/routes.php
@@ -481,6 +481,20 @@ if (!defined('CONTACT_EMAIL')) {
define('PAYMENT_STATUS_COMPLETED', 3);
define('PAYMENT_STATUS_PARTIALLY_REFUNDED', 4);
define('PAYMENT_STATUS_REFUNDED', 5);
+
+ define('CARD_UNKNOWN', 0);
+ define('CARD_AMERICAN_EXPRESS', 1);
+ define('CARD_CARTE_BLANCHE', 2);
+ define('CARD_UNIONPAY', 3);
+ define('CARD_DINERS_CLUB', 4);
+ define('CARD_DISCOVER', 5);
+ define('CARD_JCB', 6);
+ define('CARD_LASER', 7);
+ define('CARD_MAISTRO', 8);
+ define('CARD_MASTERCARD', 9);
+ define('CARD_SOLO', 10);
+ define('CARD_SWITCH', 11);
+ define('CARD_VISA', 12);
define('PAYMENT_TYPE_CREDIT', 1);
define('CUSTOM_DESIGN', 11);
diff --git a/app/Models/CardType.php b/app/Models/CardType.php
new file mode 100644
index 000000000000..6eee490c1260
--- /dev/null
+++ b/app/Models/CardType.php
@@ -0,0 +1,8 @@
+belongsTo('App\Models\PaymentStatus');
+ }
+
+ public function card_type()
+ {
+ return $this->belongsTo('App\Models\CardTypes');
}
public function getRoute()
diff --git a/app/Ninja/Repositories/PaymentRepository.php b/app/Ninja/Repositories/PaymentRepository.php
index 668b5b85d9ee..be2a9104b576 100644
--- a/app/Ninja/Repositories/PaymentRepository.php
+++ b/app/Ninja/Repositories/PaymentRepository.php
@@ -23,6 +23,7 @@ class PaymentRepository extends BaseRepository
->join('invoices', 'invoices.id', '=', 'payments.invoice_id')
->join('contacts', 'contacts.client_id', '=', 'clients.id')
->join('payment_statuses', 'payment_statuses.id', '=', 'payments.payment_status_id')
+ ->join('card_types', 'card_types.id', '=', 'payments.card_type_id')
->leftJoin('payment_types', 'payment_types.id', '=', 'payments.payment_type_id')
->leftJoin('account_gateways', 'account_gateways.id', '=', 'payments.account_gateway_id')
->leftJoin('gateways', 'gateways.id', '=', 'account_gateways.gateway_id')
@@ -54,10 +55,13 @@ class PaymentRepository extends BaseRepository
'payments.is_deleted',
'payments.user_id',
'payments.refunded',
+ 'payments.expiration',
+ 'payments.last4',
'invoices.is_deleted as invoice_is_deleted',
'gateways.name as gateway_name',
'gateways.id as gateway_id',
- 'payment_statuses.name as payment_status_name'
+ 'payment_statuses.name as payment_status_name',
+ 'card_types.code as card_type_code'
);
if (!\Session::get('show_trash:payment')) {
@@ -85,6 +89,7 @@ class PaymentRepository extends BaseRepository
->join('invoices', 'invoices.id', '=', 'payments.invoice_id')
->join('contacts', 'contacts.client_id', '=', 'clients.id')
->join('payment_statuses', 'payment_statuses.id', '=', 'payments.payment_status_id')
+ ->join('card_types', 'card_types.id', '=', 'payments.card_type_id')
->leftJoin('invitations', function ($join) {
$join->on('invitations.invoice_id', '=', 'invoices.id')
->on('invitations.contact_id', '=', 'contacts.id');
@@ -113,8 +118,11 @@ class PaymentRepository extends BaseRepository
'payment_types.name as payment_type',
'payments.account_gateway_id',
'payments.refunded',
+ 'payments.expiration',
+ 'payments.last4',
'payments.payment_status_id',
- 'payment_statuses.name as payment_status_name'
+ 'payment_statuses.name as payment_status_name',
+ 'card_types.code as card_type_code'
);
if ($filter) {
diff --git a/app/Services/BaseService.php b/app/Services/BaseService.php
index b68b06482a04..4cd0c615c024 100644
--- a/app/Services/BaseService.php
+++ b/app/Services/BaseService.php
@@ -29,12 +29,12 @@ class BaseService
return count($entities);
}
- public function createDatatable($entityType, $query, $showCheckbox = true, $hideClient = false)
+ public function createDatatable($entityType, $query, $showCheckbox = true, $hideClient = false, $orderColumns = [])
{
$columns = $this->getDatatableColumns($entityType, !$showCheckbox);
$actions = $this->getDatatableActions($entityType);
- return $this->datatableService->createDatatable($entityType, $query, $columns, $actions, $showCheckbox);
+ return $this->datatableService->createDatatable($entityType, $query, $columns, $actions, $showCheckbox, $orderColumns);
}
protected function getDatatableColumns($entityType, $hideClient)
diff --git a/app/Services/DatatableService.php b/app/Services/DatatableService.php
index 2f9af2856cf3..3d9001a095b7 100644
--- a/app/Services/DatatableService.php
+++ b/app/Services/DatatableService.php
@@ -7,11 +7,11 @@ use Auth;
class DatatableService
{
- public function createDatatable($entityType, $query, $columns, $actions = null, $showCheckbox = true)
+ public function createDatatable($entityType, $query, $columns, $actions = null, $showCheckbox = true, $orderColumns = [])
{
$table = Datatable::query($query);
- $orderColumns = [];
-
+ $calculateOrderColumns = empty($orderColumns);
+
if ($actions && $showCheckbox) {
$table->addColumn('checkbox', function ($model) {
$can_edit = Auth::user()->hasPermission('edit_all') || (isset($model->user_id) && Auth::user()->id == $model->user_id);
@@ -31,7 +31,9 @@ class DatatableService
if ($visible) {
$table->addColumn($field, $value);
- $orderColumns[] = $field;
+ if ($calculateOrderColumns) {
+ $orderColumns[] = $field;
+ }
}
}
diff --git a/app/Services/PaymentService.php b/app/Services/PaymentService.php
index 8e24370bd930..7756071348ca 100644
--- a/app/Services/PaymentService.php
+++ b/app/Services/PaymentService.php
@@ -75,7 +75,7 @@ class PaymentService extends BaseService
$data = $this->createDataForClient($invitation);
}
- $card = new CreditCard($data);
+ $card = !empty($data['number']) ? new CreditCard($data) : null;
$data = [
'amount' => $invoice->getRequestedAmount(),
'card' => $card,
@@ -155,12 +155,21 @@ class PaymentService extends BaseService
];
}
- public function createToken($gateway, $details, $accountGateway, $client, $contactId)
+ public function createToken($gateway, $details, $accountGateway, $client, $contactId, &$customerReference = null)
{
$tokenResponse = $gateway->createCard($details)->send();
- $cardReference = $tokenResponse->getCustomerReference();
+ $cardReference = $tokenResponse->getCardReference();
+ $customerReference = $tokenResponse->getCustomerReference();
+
+ if ($customerReference == $cardReference) {
+ // This customer was just created; find the card
+ $data = $tokenResponse->getData();
+ if(!empty($data['default_source'])) {
+ $cardReference = $data['default_source'];
+ }
+ }
- if ($cardReference) {
+ if ($customerReference) {
$token = AccountGatewayToken::where('client_id', '=', $client->id)
->where('account_gateway_id', '=', $accountGateway->id)->first();
@@ -172,7 +181,7 @@ class PaymentService extends BaseService
$token->client_id = $client->id;
}
- $token->token = $cardReference;
+ $token->token = $customerReference;
$token->save();
} else {
$this->lastError = $tokenResponse->getMessage();
@@ -205,7 +214,7 @@ class PaymentService extends BaseService
return $token;
}
- public function createPayment($invitation, $accountGateway, $ref, $payerId = null)
+ public function createPayment($invitation, $accountGateway, $ref, $payerId = null, $last4 = null, $expiration = null, $card_type_id = null, $routing_number = null)
{
$invoice = $invitation->invoice;
@@ -218,6 +227,10 @@ class PaymentService extends BaseService
$payment->contact_id = $invitation->contact_id;
$payment->transaction_reference = $ref;
$payment->payment_date = date_create()->format('Y-m-d');
+ $payment->last4 = $last4;
+ $payment->expiration = $expiration;
+ $payment->card_type_id = $card_type_id;
+ $payment->routing_number = $routing_number;
if ($payerId) {
$payment->payer_id = $payerId;
@@ -329,7 +342,8 @@ class PaymentService extends BaseService
$query->where('payments.user_id', '=', Auth::user()->id);
}
- return $this->createDatatable(ENTITY_PAYMENT, $query, !$clientPublicId);
+ return $this->createDatatable(ENTITY_PAYMENT, $query, !$clientPublicId, false,
+ ['invoice_number', 'transaction_reference', 'payment_type', 'amount', 'payment_date']);
}
protected function getDatatableColumns($entityType, $hideClient)
@@ -368,6 +382,15 @@ class PaymentService extends BaseService
return $model->payment_type ? $model->payment_type : ($model->account_gateway_id ? $model->gateway_name : '');
}
],
+ [
+ 'source',
+ function ($model) {
+ if (!$model->card_type_code) return '';
+ $card_type = trans("texts.card_" . $model->card_type_code);
+ $expiration = trans('texts.card_expiration', array('expires'=>Utils::fromSqlDate($model->expiration, false)->format('m/d')));
+ return '
•••'.$model->last4.' '.$expiration;
+ }
+ ],
[
'amount',
function ($model) {
diff --git a/composer.json b/composer.json
index 9bb49326b1f8..b481c5963014 100644
--- a/composer.json
+++ b/composer.json
@@ -17,7 +17,7 @@
"omnipay/mollie": "dev-master#22956c1a62a9662afa5f5d119723b413770ac525",
"omnipay/2checkout": "dev-master#e9c079c2dde0d7ba461903b3b7bd5caf6dee1248",
"omnipay/gocardless": "dev-master",
- "omnipay/stripe": "2.3.0",
+ "omnipay/stripe": "2.3.2",
"laravel/framework": "5.2.*",
"laravelcollective/html": "5.2.*",
"laravelcollective/bus": "5.2.*",
diff --git a/composer.lock b/composer.lock
index 606338333d7c..d6f9646f7c2f 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,8 +4,8 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
- "hash": "cf82d2ddb25cb1a7d6b4867bcc8692b8",
- "content-hash": "481a95753b873249aebceb99e7426421",
+ "hash": "607b25b09aee82ac769bd942fac91ba6",
+ "content-hash": "122f3f59beb2286159145d219dbcbc90",
"packages": [
{
"name": "agmscode/omnipay-agms",
@@ -127,7 +127,7 @@
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/formers/former/zipball/78ae8c65b1f8134e2db1c9491c251c03638823ca",
+ "url": "https://api.github.com/repos/formers/former/zipball/37f6876a5d211427b5c445cd64f0eb637f42f685",
"reference": "d97f907741323b390f43954a90a227921ecc6b96",
"shasum": ""
},
@@ -699,7 +699,7 @@
"laravel"
],
"abandoned": "OpenSkill/Datatable",
- "time": "2015-04-29 07:00:36"
+ "time": "2015-11-23 21:33:41"
},
{
"name": "classpreloader/classpreloader",
@@ -5661,16 +5661,16 @@
},
{
"name": "omnipay/stripe",
- "version": "v2.3.0",
+ "version": "v2.3.2",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/omnipay-stripe.git",
- "reference": "54b816a5e95e34c988d71fb805b0232cfd7c1ce5"
+ "reference": "fe05ddd73d9ae38ca026dbc270ca98aaf3f99da4"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/thephpleague/omnipay-stripe/zipball/54b816a5e95e34c988d71fb805b0232cfd7c1ce5",
- "reference": "54b816a5e95e34c988d71fb805b0232cfd7c1ce5",
+ "url": "https://api.github.com/repos/thephpleague/omnipay-stripe/zipball/fe05ddd73d9ae38ca026dbc270ca98aaf3f99da4",
+ "reference": "fe05ddd73d9ae38ca026dbc270ca98aaf3f99da4",
"shasum": ""
},
"require": {
@@ -5714,7 +5714,7 @@
"payment",
"stripe"
],
- "time": "2015-11-10 16:17:35"
+ "time": "2016-03-19 08:35:06"
},
{
"name": "omnipay/targetpay",
diff --git a/database/migrations/2016_04_23_182223_payments_changes.php b/database/migrations/2016_04_23_182223_payments_changes.php
index 641ec06e2c2d..fc887b3bb88f 100644
--- a/database/migrations/2016_04_23_182223_payments_changes.php
+++ b/database/migrations/2016_04_23_182223_payments_changes.php
@@ -13,6 +13,7 @@ class PaymentsChanges extends Migration
public function up()
{
Schema::dropIfExists('payment_statuses');
+ Schema::dropIfExists('card_types');
Schema::create('payment_statuses', function($table)
{
@@ -20,13 +21,27 @@ class PaymentsChanges extends Migration
$table->string('name');
});
+ Schema::create('card_types', function($table)
+ {
+ $table->increments('id');
+ $table->string('name');
+ $table->string('code');
+ });
+
(new \PaymentStatusSeeder())->run();
+ (new \CardTypesSeeder())->run();
Schema::table('payments', function($table)
{
$table->decimal('refunded', 13, 2);
$table->unsignedInteger('payment_status_id')->default(3);
$table->foreign('payment_status_id')->references('id')->on('payment_statuses');
+
+ $table->unsignedInteger('routing_number')->nullable();
+ $table->smallInteger('last4')->unsigned()->nullable();
+ $table->date('expiration')->nullable();
+ $table->unsignedInteger('card_type_id')->nullable();
+ $table->foreign('card_type_id')->references('id')->on('card_types');
});
}
@@ -42,8 +57,15 @@ class PaymentsChanges extends Migration
$table->dropColumn('refunded');
$table->dropForeign('payments_payment_status_id_foreign');
$table->dropColumn('payment_status_id');
+
+ $table->dropColumn('routing_number');
+ $table->dropColumn('last4');
+ $table->dropColumn('expiration');
+ $table->dropForeign('card_type_id_foreign');
+ $table->dropColumn('card_type_id');
});
Schema::dropIfExists('payment_statuses');
+ Schema::dropIfExists('card_types');
}
}
diff --git a/database/seeds/CardTypesSeeder.php b/database/seeds/CardTypesSeeder.php
new file mode 100644
index 000000000000..e5d70b9ed585
--- /dev/null
+++ b/database/seeds/CardTypesSeeder.php
@@ -0,0 +1,44 @@
+createPaymentSourceTypes();
+
+ Eloquent::reguard();
+ }
+
+ private function createPaymentSourceTypes()
+ {
+ $statuses = [
+ ['id' => '0', 'name' => 'Unknown', 'code'=>'unknown']
+ ['id' => '1', 'name' => 'American Express', 'code'=>'amex'],
+ ['id' => '2', 'name' => 'Carte Blanche', 'code'=>'carteblanche'],
+ ['id' => '3', 'name' => 'China UnionPay', 'code'=>'unionpay'],
+ ['id' => '4', 'name' => 'Diners Club', 'code'=>'diners'],
+ ['id' => '5', 'name' => 'Discover', 'code'=>'discover'],
+ ['id' => '6', 'name' => 'JCB', 'code'=>'jcb'],
+ ['id' => '7', 'name' => 'Laser', 'code'=>'laser'],
+ ['id' => '8', 'name' => 'Maestro', 'code'=>'maestro'],
+ ['id' => '9', 'name' => 'MasterCard', 'code'=>'mastercard'],
+ ['id' => '10', 'name' => 'Solo', 'code'=>'solo'],
+ ['id' => '11', 'name' => 'Switch', 'code'=>'switch'],
+ ['id' => '12', 'name' => 'Visa', 'code'=>'visa'],
+ ];
+
+ foreach ($statuses as $status) {
+ $record = CardType::find($status['id']);
+ if ($record) {
+ $record->name = $status['name'];
+ $record->save();
+ } else {
+ CardType::create($status);
+ }
+ }
+ }
+}
diff --git a/database/seeds/DatabaseSeeder.php b/database/seeds/DatabaseSeeder.php
index 753a1ce54e30..20bdbe7f0cc1 100644
--- a/database/seeds/DatabaseSeeder.php
+++ b/database/seeds/DatabaseSeeder.php
@@ -20,6 +20,7 @@ class DatabaseSeeder extends Seeder
$this->call('BanksSeeder');
$this->call('InvoiceStatusSeeder');
$this->call('PaymentStatusSeeder');
+ $this->call('CardTypesSeeder');
$this->call('CurrenciesSeeder');
$this->call('DateFormatsSeeder');
$this->call('InvoiceDesignsSeeder');
diff --git a/database/seeds/UpdateSeeder.php b/database/seeds/UpdateSeeder.php
index e5335e311b42..dffb2dbe26b7 100644
--- a/database/seeds/UpdateSeeder.php
+++ b/database/seeds/UpdateSeeder.php
@@ -16,6 +16,7 @@ class UpdateSeeder extends Seeder
$this->call('BanksSeeder');
$this->call('InvoiceStatusSeeder');
$this->call('PaymentStatusSeeder');
+ $this->call('CardTypesSeeder');
$this->call('CurrenciesSeeder');
$this->call('DateFormatsSeeder');
$this->call('InvoiceDesignsSeeder');
diff --git a/public/images/credit_cards/amex.png b/public/images/credit_cards/amex.png
new file mode 100644
index 000000000000..492d40aff6aa
Binary files /dev/null and b/public/images/credit_cards/amex.png differ
diff --git a/public/images/credit_cards/carteblanche.png b/public/images/credit_cards/carteblanche.png
new file mode 100644
index 000000000000..0b8f49aa7e5b
Binary files /dev/null and b/public/images/credit_cards/carteblanche.png differ
diff --git a/public/images/credit_cards/diners.png b/public/images/credit_cards/diners.png
new file mode 100644
index 000000000000..72e1808fb1f9
Binary files /dev/null and b/public/images/credit_cards/diners.png differ
diff --git a/public/images/credit_cards/discover.png b/public/images/credit_cards/discover.png
new file mode 100644
index 000000000000..7e36dd71508a
Binary files /dev/null and b/public/images/credit_cards/discover.png differ
diff --git a/public/images/credit_cards/jcb.png b/public/images/credit_cards/jcb.png
new file mode 100644
index 000000000000..9bb0cbafdc0e
Binary files /dev/null and b/public/images/credit_cards/jcb.png differ
diff --git a/public/images/credit_cards/laser.png b/public/images/credit_cards/laser.png
new file mode 100644
index 000000000000..2f2417942bb4
Binary files /dev/null and b/public/images/credit_cards/laser.png differ
diff --git a/public/images/credit_cards/maestro.png b/public/images/credit_cards/maestro.png
new file mode 100644
index 000000000000..38fdb4b7de1e
Binary files /dev/null and b/public/images/credit_cards/maestro.png differ
diff --git a/public/images/credit_cards/mastercard.png b/public/images/credit_cards/mastercard.png
new file mode 100644
index 000000000000..af9b1d701c96
Binary files /dev/null and b/public/images/credit_cards/mastercard.png differ
diff --git a/public/images/credit_cards/paypa.png b/public/images/credit_cards/paypa.png
new file mode 100644
index 000000000000..4fad9d864c6b
Binary files /dev/null and b/public/images/credit_cards/paypa.png differ
diff --git a/public/images/credit_cards/solo.png b/public/images/credit_cards/solo.png
new file mode 100644
index 000000000000..c236ecc0f71c
Binary files /dev/null and b/public/images/credit_cards/solo.png differ
diff --git a/public/images/credit_cards/switch.png b/public/images/credit_cards/switch.png
new file mode 100644
index 000000000000..6ff522c6c14b
Binary files /dev/null and b/public/images/credit_cards/switch.png differ
diff --git a/public/images/credit_cards/unionpay.png b/public/images/credit_cards/unionpay.png
new file mode 100644
index 000000000000..0aa7031c20f2
Binary files /dev/null and b/public/images/credit_cards/unionpay.png differ
diff --git a/public/images/credit_cards/unknown.png b/public/images/credit_cards/unknown.png
new file mode 100644
index 000000000000..0b8f49aa7e5b
Binary files /dev/null and b/public/images/credit_cards/unknown.png differ
diff --git a/public/images/credit_cards/visa.png b/public/images/credit_cards/visa.png
new file mode 100644
index 000000000000..ffd17c5a280a
Binary files /dev/null and b/public/images/credit_cards/visa.png differ
diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php
index ae717b297b23..202adb8d531e 100644
--- a/resources/lang/en/texts.php
+++ b/resources/lang/en/texts.php
@@ -1190,7 +1190,21 @@ $LANG = array(
'status_refunded' => 'Refunded',
'refunded_payment' => 'Refunded Payment',
'activity_39' => ':user refunded :adjustment of a :payment_amount payment (:payment)',
-
+ 'card_expiration' => 'Exp: :expires',
+
+ 'card_unknown' => 'Unknown',
+ 'card_amex' => 'American Express',
+ 'card_carteblanche' => 'Carte Blanche',
+ 'card_unionpay' => 'UnionPay',
+ 'card_diners' => 'Diners Club',
+ 'card_discover' => 'Discover',
+ 'card_jcb' => 'JCB',
+ 'card_laser' => 'Laser',
+ 'card_maestro' => 'Maestro',
+ 'card_mastercard' => 'MasterCard',
+ 'card_solo' => 'Solo',
+ 'card_switch' => 'Switch',
+ 'card_visa' => 'Visa',
);
return $LANG;
diff --git a/resources/views/clients/show.blade.php b/resources/views/clients/show.blade.php
index 0e434b3d07d4..db49da710efe 100644
--- a/resources/views/clients/show.blade.php
+++ b/resources/views/clients/show.blade.php
@@ -290,8 +290,10 @@
trans('texts.invoice'),
trans('texts.transaction_reference'),
trans('texts.method'),
+ trans('texts.source'),
trans('texts.payment_amount'),
- trans('texts.payment_date'))
+ trans('texts.payment_date'),
+ trans('texts.status'))
->setUrl(url('api/payments/' . $client->public_id))
->setCustomValues('entityType', 'payments')
->setOptions('sPaginationType', 'bootstrap')