Merge branch 'develop' of github.com:hillelcoren/invoice-ninja into develop

Conflicts:
	app/Http/routes.php
	resources/lang/en/texts.php
This commit is contained in:
Hillel Coren 2016-05-16 11:34:00 +03:00
commit a82437b2bc
34 changed files with 1246 additions and 507 deletions

View File

@ -67,4 +67,12 @@ API_SECRET=password
#MAX_DOCUMENT_SIZE # KB
#MAX_EMAIL_DOCUMENTS_SIZE # Total KB
#MAX_ZIP_DOCUMENTS_SIZE # Total KB (uncompressed)
#DOCUMENT_PREVIEW_SIZE # Pixels
#DOCUMENT_PREVIEW_SIZE # Pixels
WEPAY_CLIENT_ID=
WEPAY_CLIENT_SECRET=
WEPAY_AUTO_UPDATE=true # Requires permission from WePay
WEPAY_ENVIRONMENT=production # production or stage
# See https://www.wepay.com/developer/reference/structures#theme
WEPAY_THEME={"name":"Invoice Ninja","primary_color":"0b4d78","secondary_color":"0b4d78","background_color":"f8f8f8","button_color":"33b753"}'));

View File

@ -1,5 +1,6 @@
<?php namespace App\Http\Controllers;
use App\Models\AccountGateway;
use Auth;
use File;
use Image;
@ -412,6 +413,7 @@ class AccountController extends BaseController
$account = Auth::user()->account;
$account->load('account_gateways');
$count = count($account->account_gateways);
$trashedCount = AccountGateway::scope($account->id)->withTrashed()->count();
if ($accountGateway = $account->getGatewayConfig(GATEWAY_STRIPE)) {
if (! $accountGateway->getPublishableStripeKey()) {
@ -419,10 +421,17 @@ class AccountController extends BaseController
}
}
if ($count == 0) {
if ($trashedCount == 0) {
return Redirect::to('gateways/create');
} else {
$switchToWepay = WEPAY_CLIENT_ID && !$account->getGatewayConfig(GATEWAY_WEPAY);
if ($switchToWepay && $account->token_billing_type_id != TOKEN_BILLING_DISABLED) {
$switchToWepay = !$account->getGatewayConfig(GATEWAY_BRAINTREE) && !$account->getGatewayConfig(GATEWAY_STRIPE);
}
return View::make('accounts.payments', [
'showSwitchToWepay' => $switchToWepay,
'showAdd' => $count < count(Gateway::$paymentTypes),
'title' => trans('texts.online_payments')
]);

View File

@ -11,6 +11,7 @@ use Validator;
use stdClass;
use URL;
use Utils;
use WePay;
use App\Models\Gateway;
use App\Models\Account;
use App\Models\AccountGateway;
@ -57,7 +58,6 @@ class AccountGatewayController extends BaseController
$data['paymentTypeId'] = $accountGateway->getPaymentType();
$data['selectGateways'] = Gateway::where('id', '=', $accountGateway->gateway_id)->get();
return View::make('accounts.account_gateway', $data);
}
@ -101,35 +101,12 @@ class AccountGatewayController extends BaseController
private function getViewModel($accountGateway = false)
{
$selectedCards = $accountGateway ? $accountGateway->accepted_credit_cards : 0;
$account = Auth::user()->account;
$user = Auth::user();
$account =$user->account;
$paymentTypes = [];
foreach (Gateway::$paymentTypes as $type) {
if ($accountGateway || !$account->getGatewayByType($type)) {
if ($type == PAYMENT_TYPE_CREDIT_CARD && $account->getGatewayByType(PAYMENT_TYPE_STRIPE)) {
// Stripe is already handling credit card payments
continue;
}
if ($type == PAYMENT_TYPE_STRIPE && $account->getGatewayByType(PAYMENT_TYPE_CREDIT_CARD)) {
// Another gateway is already handling credit card payments
continue;
}
if ($type == PAYMENT_TYPE_DIRECT_DEBIT && $stripeGateway = $account->getGatewayByType(PAYMENT_TYPE_STRIPE)) {
if (!empty($stripeGateway->getAchEnabled())) {
// Stripe is already handling ACH payments
continue;
}
}
if ($type == PAYMENT_TYPE_PAYPAL && $braintreeGateway = $account->getGatewayConfig(GATEWAY_BRAINTREE)) {
if (!empty($braintreeGateway->getPayPalEnabled())) {
// PayPal is already enabled
continue;
}
}
if ($accountGateway || $account->canAddGateway($type)) {
$paymentTypes[$type] = $type == PAYMENT_TYPE_CREDIT_CARD ? trans('texts.other_providers'): trans('texts.'.strtolower($type));
if ($type == PAYMENT_TYPE_BITCOIN) {
@ -158,7 +135,7 @@ class AccountGatewayController extends BaseController
foreach ($gateways as $gateway) {
$fields = $gateway->getFields();
asort($fields);
$gateway->fields = $fields;
$gateway->fields = $gateway->id == GATEWAY_WEPAY ? [] : $fields;
if ($accountGateway && $accountGateway->gateway_id == $gateway->id) {
$accountGateway->fields = $gateway->fields;
}
@ -172,6 +149,7 @@ class AccountGatewayController extends BaseController
return [
'paymentTypes' => $paymentTypes,
'account' => $account,
'user' => $user,
'accountGateway' => $accountGateway,
'config' => false,
'gateways' => $gateways,
@ -188,7 +166,7 @@ class AccountGatewayController extends BaseController
$ids = Input::get('bulk_public_id');
$count = $this->accountGatewayService->bulk($ids, $action);
Session::flash('message', trans('texts.archived_account_gateway'));
Session::flash('message', trans("texts.{$action}d_account_gateway"));
return Redirect::to('settings/' . ACCOUNT_PAYMENTS);
}
@ -236,14 +214,16 @@ class AccountGatewayController extends BaseController
}
}
foreach ($fields as $field => $details) {
if (!in_array($field, $optional)) {
if (strtolower($gateway->name) == 'beanstream') {
if (in_array($field, ['merchant_id', 'passCode'])) {
$rules[$gateway->id.'_'.$field] = 'required';
if ($gatewayId != GATEWAY_WEPAY) {
foreach ($fields as $field => $details) {
if (!in_array($field, $optional)) {
if (strtolower($gateway->name) == 'beanstream') {
if (in_array($field, ['merchant_id', 'passCode'])) {
$rules[$gateway->id . '_' . $field] = 'required';
}
} else {
$rules[$gateway->id . '_' . $field] = 'required';
}
} else {
$rules[$gateway->id.'_'.$field] = 'required';
}
}
}
@ -265,20 +245,32 @@ class AccountGatewayController extends BaseController
} else {
$accountGateway = AccountGateway::createNew();
$accountGateway->gateway_id = $gatewayId;
if ($gatewayId == GATEWAY_WEPAY) {
if(!$this->setupWePay($accountGateway, $wepayResponse)) {
return $wepayResponse;
}
$oldConfig = $accountGateway->getConfig();
}
}
$config = new stdClass();
foreach ($fields as $field => $details) {
$value = trim(Input::get($gateway->id.'_'.$field));
// if the new value is masked use the original value
if ($oldConfig && $value && $value === str_repeat('*', strlen($value))) {
$value = $oldConfig->$field;
}
if (!$value && ($field == 'testMode' || $field == 'developerMode')) {
// do nothing
} else {
$config->$field = $value;
if ($gatewayId != GATEWAY_WEPAY) {
foreach ($fields as $field => $details) {
$value = trim(Input::get($gateway->id . '_' . $field));
// if the new value is masked use the original value
if ($oldConfig && $value && $value === str_repeat('*', strlen($value))) {
$value = $oldConfig->$field;
}
if (!$value && ($field == 'testMode' || $field == 'developerMode')) {
// do nothing
} else {
$config->$field = $value;
}
}
} elseif($oldConfig) {
$config = clone $oldConfig;
}
$publishableKey = Input::get('publishable_key');
@ -340,16 +332,163 @@ class AccountGatewayController extends BaseController
$account->save();
}
if ($accountGatewayPublicId) {
$message = trans('texts.updated_gateway');
if(isset($wepayResponse)) {
return $wepayResponse;
} else {
$message = trans('texts.created_gateway');
if ($accountGatewayPublicId) {
$message = trans('texts.updated_gateway');
} else {
$message = trans('texts.created_gateway');
}
Session::flash('message', $message);
return Redirect::to("gateways/{$accountGateway->public_id}/edit");
}
Session::flash('message', $message);
return Redirect::to("gateways/{$accountGateway->public_id}/edit");
}
}
protected function getWePayUpdateUri($accountGateway)
{
if ($accountGateway->gateway_id != GATEWAY_WEPAY) {
return null;
}
$wepay = Utils::setupWePay($accountGateway);
$update_uri_data = $wepay->request('account/get_update_uri', array(
'account_id' => $accountGateway->getConfig()->accountId,
'mode' => 'iframe',
'redirect_uri' => URL::to('/gateways'),
));
return $update_uri_data->uri;
}
protected function setupWePay($accountGateway, &$response)
{
$user = Auth::user();
$account = $user->account;
$validator = Validator::make(Input::all(), array(
'company_name' => 'required',
'description' => 'required',
'tos_agree' => 'required',
'first_name' => 'required',
'last_name' => 'required',
'email' => 'required',
));
if ($validator->fails()) {
return Redirect::to('gateways/create')
->withErrors($validator)
->withInput();
}
try{
$wepay = Utils::setupWePay();
$wepayUser = $wepay->request('user/register/', array(
'client_id' => WEPAY_CLIENT_ID,
'client_secret' => WEPAY_CLIENT_SECRET,
'email' => Input::get('email'),
'first_name' => Input::get('first_name'),
'last_name' => Input::get('last_name'),
'original_ip' => \Request::getClientIp(true),
'original_device' => \Request::server('HTTP_USER_AGENT'),
'tos_acceptance_time' => time(),
'redirect_uri' => URL::to('gateways'),
'callback_uri' => URL::to(env('WEBHOOK_PREFIX','').'paymenthook/'.$account->account_key.'/'.GATEWAY_WEPAY),
'scope' => 'manage_accounts,collect_payments,view_user,preapprove_payments,send_money',
));
$accessToken = $wepayUser->access_token;
$accessTokenExpires = $wepayUser->expires_in ? (time() + $wepayUser->expires_in) : null;
$wepay = new WePay($accessToken);
$wepayAccount = $wepay->request('account/create/', array(
'name' => Input::get('company_name'),
'description' => Input::get('description'),
'theme_object' => json_decode(WEPAY_THEME),
));
try {
$wepay->request('user/send_confirmation/', []);
$confirmationRequired = true;
} catch(\WePayException $ex){
if ($ex->getMessage() == 'This access_token is already approved.') {
$confirmationRequired = false;
} else {
throw $ex;
}
}
if (($gateway = $account->getGatewayByType(PAYMENT_TYPE_CREDIT_CARD)) || ($gateway = $account->getGatewayByType(PAYMENT_TYPE_STRIPE))) {
$gateway->delete();
}
$accountGateway->gateway_id = GATEWAY_WEPAY;
$accountGateway->setConfig(array(
'userId' => $wepayUser->user_id,
'accessToken' => $accessToken,
'tokenType' => $wepayUser->token_type,
'tokenExpires' => $accessTokenExpires,
'accountId' => $wepayAccount->account_id,
'testMode' => WEPAY_ENVIRONMENT == WEPAY_STAGE,
));
if ($confirmationRequired) {
Session::flash('message', trans('texts.created_wepay_confirmation_required'));
} else {
$updateUri = $wepay->request('/account/get_update_uri', array(
'account_id' => $wepayAccount->account_id,
'redirect_uri' => URL::to('gateways'),
));
$response = Redirect::to($updateUri->uri);
return true;
}
$response = Redirect::to("gateways/{$accountGateway->public_id}/edit");
return true;
} catch (\WePayException $e) {
Session::flash('error', $e->getMessage());
$response = Redirect::to('gateways/create')
->withInput();
return false;
}
}
public function resendConfirmation($publicId = false)
{
$accountGateway = AccountGateway::scope($publicId)->firstOrFail();
if ($accountGateway->gateway_id == GATEWAY_WEPAY) {
try {
$wepay = Utils::setupWePay($accountGateway);
$wepay->request('user/send_confirmation', []);
Session::flash('message', trans('texts.resent_confirmation_email'));
} catch (\WePayException $e) {
Session::flash('error', $e->getMessage());
}
}
return Redirect::to("gateways/{$accountGateway->public_id}/edit");
}
public function switchToWepay()
{
$data = self::getViewModel();
$data['url'] = 'gateways';
$data['method'] = 'POST';
unset($data['gateways']);
if ( ! \Request::secure() && ! Utils::isNinjaDev()) {
Session::flash('warning', trans('texts.enable_https'));
}
return View::make('accounts.account_gateway_switch_wepay', $data);
}
}

View File

@ -1,7 +1,7 @@
<?php namespace App\Http\Controllers;
use App\Models\Expense;
use app\Ninja\Repositories\ExpenseRepository;
use App\Ninja\Repositories\ExpenseRepository;
use App\Ninja\Transformers\ExpenseTransformer;
use App\Services\ExpenseService;
use Utils;

View File

@ -152,6 +152,8 @@ class PaymentController extends BaseController
$data = array();
Session::put($invitation->id.'payment_ref', $invoice->id.'_'.uniqid());
if ($paymentType != PAYMENT_TYPE_BRAINTREE_PAYPAL) {
if ($paymentType == PAYMENT_TYPE_TOKEN) {
$useToken = true;
@ -198,6 +200,10 @@ class PaymentController extends BaseController
$data['braintreeClientToken'] = $this->paymentService->getBraintreeClientToken($account);
}
if(!empty($data['braintreeClientToken']) || $accountGateway->getPublishableStripeKey()|| $accountGateway->gateway_id == GATEWAY_WEPAY) {
$data['tokenize'] = true;
}
} else {
if ($deviceData = Input::get('details')) {
Session::put($invitation->id . 'device_data', $deviceData);
@ -405,7 +411,7 @@ class PaymentController extends BaseController
'last_name' => 'required',
];
if ( ! Input::get('stripeToken') && ! Input::get('payment_method_nonce') && !(Input::get('plaidPublicToken') && Input::get('plaidAccountId'))) {
if ( !Input::get('sourceToken') && !(Input::get('plaidPublicToken') && Input::get('plaidAccountId'))) {
$rules = array_merge(
$rules,
[
@ -433,7 +439,7 @@ class PaymentController extends BaseController
$validator = Validator::make(Input::all(), $rules);
if ($validator->fails()) {
return false;
return $validator;
}
if ($requireAddress && $accountGateway->update_address) {
@ -460,8 +466,6 @@ class PaymentController extends BaseController
$accountGateway = $account->getGatewayByType($paymentType);
$paymentMethod = null;
if ($useToken) {
if(!$sourceId) {
Session::flash('error', trans('texts.no_payment_method_specified'));
@ -473,12 +477,13 @@ class PaymentController extends BaseController
}
}
if (!static::processPaymentClientDetails($client, $accountGateway, $paymentType, $onSite)) {
if (($validator = static::processPaymentClientDetails($client, $accountGateway, $paymentType, $onSite)) !== true) {
return Redirect::to('payment/'.$invitationKey)
->withErrors($validator)
->withInput(Request::except('cvv'));
}
try {
// For offsite payments send the client's details on file
// If we're using a token then we don't need to send any other data
@ -492,21 +497,48 @@ class PaymentController extends BaseController
$details = $this->paymentService->getPaymentDetails($invitation, $accountGateway, $data);
// check if we're creating/using a billing token
$tokenBillingSupported = false;
$sourceReferenceParam = 'token';
if ($accountGateway->gateway_id == GATEWAY_STRIPE) {
$tokenBillingSupported = true;
$customerReferenceParam = 'customerReference';
if ($paymentType == PAYMENT_TYPE_STRIPE_ACH && !Input::get('authorize_ach')) {
Session::flash('error', trans('texts.ach_authorization_required'));
return Redirect::to('payment/'.$invitationKey)->withInput(Request::except('cvv'));
}
} elseif ($accountGateway->gateway_id == GATEWAY_BRAINTREE) {
$tokenBillingSupported = true;
$sourceReferenceParam = 'paymentMethodToken';
$customerReferenceParam = 'customerId';
$deviceData = Input::get('device_data');
if (!$deviceData) {
$deviceData = Session::get($invitation->id . 'device_data');
}
if($deviceData) {
$details['device_data'] = $deviceData;
}
} elseif ($accountGateway->gateway_id == GATEWAY_WEPAY) {
$tokenBillingSupported = true;
$customerReferenceParam = false;
}
if ($tokenBillingSupported) {
if ($useToken) {
$details['customerReference'] = $customerReference;
unset($details['token']);
$details['cardReference'] = $sourceReference;
if ($customerReferenceParam) {
$details[$customerReferenceParam] = $customerReference;
}
$details[$sourceReferenceParam] = $sourceReference;
unset($details['card']);
} elseif ($account->token_billing_type_id == TOKEN_BILLING_ALWAYS || Input::get('token_billing') || $paymentType == PAYMENT_TYPE_STRIPE_ACH) {
$token = $this->paymentService->createToken($gateway, $details, $accountGateway, $client, $invitation->contact_id, $customerReference/* return parameter */, $paymentMethod/* return parameter */);
if ($token) {
$details['token'] = $token;
$details['customerReference'] = $customerReference;
$details[$sourceReferenceParam] = $token;
if ($customerReferenceParam) {
$details[$customerReferenceParam] = $customerReference;
}
if ($paymentType == PAYMENT_TYPE_STRIPE_ACH && empty(Input::get('plaidPublicToken')) ) {
// The user needs to complete verification
@ -518,36 +550,6 @@ class PaymentController extends BaseController
return Redirect::to('payment/'.$invitationKey)->withInput(Request::except('cvv'));
}
}
} elseif ($accountGateway->gateway_id == GATEWAY_BRAINTREE) {
$deviceData = Input::get('device_data');
if (!$deviceData) {
$deviceData = Session::get($invitation->id . 'device_data');
}
if ($token = Input::get('payment_method_nonce')) {
$details['token'] = $token;
unset($details['card']);
}
if ($useToken) {
$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 */, $paymentMethod/* return parameter */);
if ($token) {
$details['paymentMethodToken'] = $token;
$details['customerId'] = $customerReference;
unset($details['token']);
} else {
$this->error('Token-No-Ref', $this->paymentService->lastError, $accountGateway);
return Redirect::to('payment/'.$invitationKey)->withInput(Request::except('cvv'));
}
}
if($deviceData) {
$details['deviceData'] = $deviceData;
}
}
$response = $gateway->purchase($details)->send();
@ -569,7 +571,7 @@ class PaymentController extends BaseController
if (!$ref) {
$this->error('No-Ref', $response->getMessage(), $accountGateway);
if ($onSite) {
if ($onSite && $paymentType != PAYMENT_TYPE_BRAINTREE_PAYPAL) {
return Redirect::to('payment/'.$invitationKey)
->withInput(Request::except('cvv'));
} else {
@ -597,7 +599,7 @@ class PaymentController extends BaseController
$response->redirect();
} else {
$this->error('Unknown', $response->getMessage(), $accountGateway);
if ($onSite) {
if ($onSite && $paymentType != PAYMENT_TYPE_BRAINTREE_PAYPAL) {
return Redirect::to('payment/'.$invitationKey)->withInput(Request::except('cvv'));
} else {
return Redirect::to('view/'.$invitationKey);
@ -605,7 +607,7 @@ class PaymentController extends BaseController
}
} catch (\Exception $e) {
$this->error('Uncaught', false, $accountGateway, $e);
if ($onSite) {
if ($onSite && $paymentType != PAYMENT_TYPE_BRAINTREE_PAYPAL) {
return Redirect::to('payment/'.$invitationKey)->withInput(Request::except('cvv'));
} else {
return Redirect::to('view/'.$invitationKey);
@ -760,7 +762,7 @@ class PaymentController extends BaseController
'message' => $data,
], 500);
} elseif (!empty($data)) {
return $data;
return response()->json($data);
}
return response()->json([
@ -791,6 +793,8 @@ class PaymentController extends BaseController
switch($gatewayId) {
case GATEWAY_STRIPE:
return $this->handleStripeWebhook($accountGateway);
case GATEWAY_WEPAY:
return $this->handleWePayWebhook($accountGateway);
default:
return response()->json([
'message' => 'Unsupported gateway',
@ -798,6 +802,50 @@ class PaymentController extends BaseController
}
}
protected function handleWePayWebhook($accountGateway) {
$data = Input::all();
$accountId = $accountGateway->account_id;
foreach (array_keys($data) as $key) {
if ('_id' == substr($key, -3)) {
$objectType = substr($key, 0, -3);
$objectId = $data[$key];
break;
}
}
if (!isset($objectType)) {
return response()->json([
'message' => 'Could not find object id parameter',
], 400);
}
if ($objectType == 'credit_card') {
$paymentMethod = PaymentMethod::scope(false, $accountId)->where('source_reference', '=', $objectId)->first();
if (!$paymentMethod) {
return array('message' => 'Unknown payment method');
}
$wepay = \Utils::setupWePay($accountGateway);
$source = $wepay->request('credit_card', array(
'client_id' => WEPAY_CLIENT_ID,
'client_secret' => WEPAY_CLIENT_SECRET,
'credit_card_id' => intval($objectId),
));
if ($source->state == 'deleted') {
$paymentMethod->delete();
} else {
$this->paymentService->convertPaymentMethodFromWePay($source, null, $paymentMethod)->save();
}
return array('message' => 'Processed successfully');
} else {
return array('message' => 'Ignoring event');
}
}
protected function handleStripeWebhook($accountGateway) {
$eventId = Input::get('id');
$eventType= Input::get('type');

View File

@ -16,7 +16,7 @@ use Redirect;
use App\Models\Gateway;
use App\Models\Invitation;
use App\Models\Document;
use App\ModelsPaymentMethod;
use App\Models\PaymentMethod;
use App\Ninja\Repositories\InvoiceRepository;
use App\Ninja\Repositories\PaymentRepository;
use App\Ninja\Repositories\ActivityRepository;
@ -176,8 +176,10 @@ class PublicClientController extends BaseController
$html = '';
if ($paymentMethod->payment_type_id == PAYMENT_TYPE_ACH) {
if($paymentMethod->bank_data) {
if ($paymentMethod->bank_data) {
$html = '<div>' . htmlentities($paymentMethod->bank_data->name) . '</div>';
} else {
$html = '<img height="22" src="'.URL::to('/images/credit_cards/ach.png').'" style="float:left" alt="'.trans("texts.direct_debit").'">';
}
} elseif ($paymentMethod->payment_type_id == PAYMENT_TYPE_ID_PAYPAL) {
$html = '<img height="22" src="'.URL::to('/images/credit_cards/paypal.png').'" alt="'.trans("texts.card_".$code).'">';
@ -860,7 +862,6 @@ class PublicClientController extends BaseController
];
if ($paymentType == PAYMENT_TYPE_STRIPE_ACH) {
$data['currencies'] = Cache::get('currencies');
}
@ -868,6 +869,10 @@ class PublicClientController extends BaseController
$data['braintreeClientToken'] = $this->paymentService->getBraintreeClientToken($account);
}
if(!empty($data['braintreeClientToken']) || $accountGateway->getPublishableStripeKey()|| $accountGateway->gateway_id == GATEWAY_WEPAY) {
$data['tokenize'] = true;
}
return View::make('payments.add_paymentmethod', $data);
}
@ -883,10 +888,12 @@ class PublicClientController extends BaseController
$account = $client->account;
$accountGateway = $account->getGatewayByType($paymentType);
$sourceToken = $accountGateway->gateway_id == GATEWAY_STRIPE ? Input::get('stripeToken'):Input::get('payment_method_nonce');
$sourceToken = Input::get('sourceToken');
if (!PaymentController::processPaymentClientDetails($client, $accountGateway, $paymentType)) {
return Redirect::to('client/paymentmethods/add/' . $typeLink)->withInput(Request::except('cvv'));
if (($validator = PaymentController::processPaymentClientDetails($client, $accountGateway, $paymentType)) !== true) {
return Redirect::to('client/paymentmethods/add/' . $typeLink)
->withErrors($validator)
->withInput(Request::except('cvv'));
}
if ($sourceToken) {

View File

@ -242,6 +242,8 @@ Route::group([
Route::post('/import_csv', 'ImportController@doImportCSV');
Route::resource('gateways', 'AccountGatewayController');
Route::get('gateways/{public_id}/resend_confirmation', 'AccountGatewayController@resendConfirmation');
Route::get('gateways/switch/wepay', 'AccountGatewayController@switchToWepay');
Route::get('api/gateways', array('as'=>'api.gateways', 'uses'=>'AccountGatewayController@getDatatable'));
Route::post('account_gateways/bulk', 'AccountGatewayController@bulk');
@ -563,6 +565,10 @@ if (!defined('CONTACT_EMAIL')) {
define('GATEWAY_WEPAY', 60);
define('GATEWAY_BRAINTREE', 61);
// The customer exists, but only as a local concept
// The remote gateway doesn't understand the concept of customers
define('CUSTOMER_REFERENCE_LOCAL', 'local');
define('EVENT_CREATE_CLIENT', 1);
define('EVENT_CREATE_INVOICE', 2);
define('EVENT_CREATE_QUOTE', 3);
@ -704,10 +710,10 @@ if (!defined('CONTACT_EMAIL')) {
define('RESELLER_REVENUE_SHARE', 'A');
define('RESELLER_LIMITED_USERS', 'B');
define('AUTO_BILL_OFF', 0);
define('AUTO_BILL_OPT_IN', 1);
define('AUTO_BILL_OPT_OUT', 2);
define('AUTO_BILL_ALWAYS', 3);
define('AUTO_BILL_OFF', 1);
define('AUTO_BILL_OPT_IN', 2);
define('AUTO_BILL_OPT_OUT', 3);
define('AUTO_BILL_ALWAYS', 4);
// These must be lowercase
define('PLAN_FREE', 'free');
@ -748,6 +754,15 @@ if (!defined('CONTACT_EMAIL')) {
// Pro users who started paying on or before this date will be able to manage users
define('PRO_USERS_GRANDFATHER_DEADLINE', '2016-05-15');
// WePay
define('WEPAY_PRODUCTION', 'production');
define('WEPAY_STAGE', 'stage');
define('WEPAY_CLIENT_ID', env('WEPAY_CLIENT_ID'));
define('WEPAY_CLIENT_SECRET', env('WEPAY_CLIENT_SECRET'));
define('WEPAY_AUTO_UPDATE', env('WEPAY_AUTO_UPDATE', false));
define('WEPAY_ENVIRONMENT', env('WEPAY_ENVIRONMENT', WEPAY_PRODUCTION));
define('WEPAY_THEME', env('WEPAY_THEME','{"name":"Invoice Ninja","primary_color":"0b4d78","secondary_color":"0b4d78","background_color":"f8f8f8","button_color":"33b753"}'));
$creditCards = [
1 => ['card' => 'images/credit_cards/Test-Visa-Icon.png', 'text' => 'Visa'],
2 => ['card' => 'images/credit_cards/Test-MasterCard-Icon.png', 'text' => 'Master Card'],

View File

@ -15,6 +15,7 @@ use Log;
use DateTime;
use stdClass;
use Carbon;
use WePay;
use App\Models\Currency;
@ -995,4 +996,21 @@ class Utils
return $url;
}
public static function setupWePay($accountGateway = null)
{
if (WePay::getEnvironment() == 'none') {
if (WEPAY_ENVIRONMENT == WEPAY_STAGE) {
WePay::useStaging(WEPAY_CLIENT_ID, WEPAY_CLIENT_SECRET);
} else {
WePay::useProduction(WEPAY_CLIENT_ID, WEPAY_CLIENT_SECRET);
}
}
if ($accountGateway) {
return new WePay($accountGateway->getConfig()->accessToken);
} else {
return new WePay(null);
}
}
}

View File

@ -307,8 +307,8 @@ class ActivityListener
$this->activityRepo->create(
$payment,
ACTIVITY_TYPE_DELETE_PAYMENT,
$payment->amount,
$payment->amount * -1
$payment->amount - $payment->refunded,
($payment->amount - $payment->refunded) * -1
);
}
@ -343,8 +343,8 @@ class ActivityListener
$this->activityRepo->create(
$payment,
ACTIVITY_TYPE_FAILED_PAYMENT,
$payment->amount,
$payment->amount * -1
($payment->amount - $payment->refunded),
($payment->amount - $payment->refunded) * -1
);
}
@ -367,8 +367,8 @@ class ActivityListener
$this->activityRepo->create(
$payment,
ACTIVITY_TYPE_RESTORE_PAYMENT,
$event->fromDeleted ? $payment->amount * -1 : 0,
$event->fromDeleted ? $payment->amount : 0
$event->fromDeleted ? ($payment->amount - $payment->refunded) * -1 : 0,
$event->fromDeleted ? ($payment->amount - $payment->refunded) : 0
);
}
}

View File

@ -1245,6 +1245,8 @@ class Account extends Eloquent
return GATEWAY_STRIPE;
} elseif ($this->isGatewayConfigured(GATEWAY_BRAINTREE)) {
return GATEWAY_BRAINTREE;
} elseif ($this->isGatewayConfigured(GATEWAY_WEPAY)) {
return GATEWAY_WEPAY;
} else {
return false;
}
@ -1410,6 +1412,37 @@ class Account extends Eloquent
public function getFontFolders(){
return array_map(function($item){return $item['folder'];}, $this->getFontsData());
}
public function canAddGateway($type){
if($this->getGatewayByType($type)) {
return false;
}
if ($type == PAYMENT_TYPE_CREDIT_CARD && $this->getGatewayByType(PAYMENT_TYPE_STRIPE)) {
// Stripe is already handling credit card payments
return false;
}
if ($type == PAYMENT_TYPE_STRIPE && $this->getGatewayByType(PAYMENT_TYPE_CREDIT_CARD)) {
// Another gateway is already handling credit card payments
return false;
}
if ($type == PAYMENT_TYPE_DIRECT_DEBIT && $stripeGateway = $this->getGatewayByType(PAYMENT_TYPE_STRIPE)) {
if (!empty($stripeGateway->getAchEnabled())) {
// Stripe is already handling ACH payments
return false;
}
}
if ($type == PAYMENT_TYPE_PAYPAL && $braintreeGateway = $this->getGatewayConfig(GATEWAY_BRAINTREE)) {
if (!empty($braintreeGateway->getPayPalEnabled())) {
// PayPal is already enabled
return false;
}
}
return true;
}
}
Account::updated(function ($account) {

View File

@ -77,7 +77,7 @@ class AccountGateway extends EntityModel
return !empty($this->getConfigField('enableAch'));
}
public function getPayPAlEnabled()
public function getPayPalEnabled()
{
return !empty($this->getConfigField('enablePayPal'));
}

View File

@ -167,7 +167,7 @@ class Payment extends EntityModel
return ENTITY_PAYMENT;
}
public function getBankData()
public function getBankDataAttribute()
{
if (!$this->routing_number) {
return null;

View File

@ -63,7 +63,7 @@ class PaymentMethod extends EntityModel
return $this->hasMany('App\Models\Payments');
}
public function getBankData()
public function getBankDataAttribute()
{
if (!$this->routing_number) {
return null;

View File

@ -15,10 +15,14 @@ class AccountGatewayRepository extends BaseRepository
public function find($accountId)
{
return DB::table('account_gateways')
$query = DB::table('account_gateways')
->join('gateways', 'gateways.id', '=', 'account_gateways.gateway_id')
->where('account_gateways.deleted_at', '=', null)
->where('account_gateways.account_id', '=', $accountId)
->select('account_gateways.public_id', 'gateways.name', 'account_gateways.deleted_at', 'account_gateways.gateway_id');
->where('account_gateways.account_id', '=', $accountId);
if (!\Session::get('show_trash:gateway')) {
$query->where('account_gateways.deleted_at', '=', null);
}
return $query->select('account_gateways.id', 'account_gateways.public_id', 'gateways.name', 'account_gateways.deleted_at', 'account_gateways.gateway_id');
}
}

View File

@ -324,7 +324,7 @@ class InvoiceRepository extends BaseRepository
$invoice->start_date = Utils::toSqlDate($data['start_date']);
$invoice->end_date = Utils::toSqlDate($data['end_date']);
$invoice->client_enable_auto_bill = isset($data['client_enable_auto_bill']) && $data['client_enable_auto_bill'] ? true : false;
$invoice->auto_bill = isset($data['auto_bill']) ? intval($data['auto_bill']) : 0;
$invoice->auto_bill = isset($data['auto_bill']) ? intval($data['auto_bill']) : AUTO_BILL_OFF;
if ($invoice->auto_bill < AUTO_BILL_OFF || $invoice->auto_bill > AUTO_BILL_ALWAYS ) {
$invoice->auto_bill = AUTO_BILL_OFF;

View File

@ -2,6 +2,7 @@
use URL;
use App\Models\Gateway;
use App\Models\AccountGateway;
use App\Services\BaseService;
use App\Ninja\Repositories\AccountGatewayRepository;
@ -41,7 +42,40 @@ class AccountGatewayService extends BaseService
[
'name',
function ($model) {
return link_to("gateways/{$model->public_id}/edit", $model->name)->toHtml();
if ($model->deleted_at) {
return $model->name;
} elseif ($model->gateway_id != GATEWAY_WEPAY) {
return link_to("gateways/{$model->public_id}/edit", $model->name)->toHtml();
} else {
$accountGateway = AccountGateway::find($model->id);
$endpoint = WEPAY_ENVIRONMENT == WEPAY_STAGE ? 'https://stage.wepay.com/' : 'https://www.wepay.com/';
$wepayAccountId = $accountGateway->getConfig()->accountId;
$linkText = $model->name;
$url = $endpoint.'account/'.$wepayAccountId;
$wepay = \Utils::setupWepay($accountGateway);
$html = link_to($url, $linkText, array('target'=>'_blank'))->toHtml();
try {
$wepayAccount = $wepay->request('/account', array('account_id' => $wepayAccountId));
if ($wepayAccount->state == 'action_required') {
$updateUri = $wepay->request('/account/get_update_uri', array(
'account_id' => $wepayAccountId,
'redirect_uri' => URL::to('gateways'),
));
$linkText .= ' <span style="color:#d9534f">('.trans('texts.action_required').')</span>';
$url = $updateUri->uri;
$html = "<a href=\"{$url}\">{$linkText}</a>";
$model->setupUrl = $url;
} elseif ($wepayAccount->state == 'pending') {
$linkText .= ' ('.trans('texts.resend_confirmation_email').')';
$model->resendConfirmationUrl = $url = URL::to("gateways/{$accountGateway->public_id}/resend_confirmation");
$html = link_to($url, $linkText)->toHtml();
}
} catch(\WePayException $ex){}
return $html;
}
}
],
[
@ -57,9 +91,41 @@ class AccountGatewayService extends BaseService
{
return [
[
uctrans('texts.resend_confirmation_email'),
function ($model) {
return $model->resendConfirmationUrl;
},
function($model) {
return !$model->deleted_at && $model->gateway_id == GATEWAY_WEPAY && !empty($model->resendConfirmationUrl);
}
], [
uctrans('texts.finish_setup'),
function ($model) {
return $model->setupUrl;
},
function($model) {
return !$model->deleted_at && $model->gateway_id == GATEWAY_WEPAY && !empty($model->setupUrl);
}
] , [
uctrans('texts.edit_gateway'),
function ($model) {
return URL::to("gateways/{$model->public_id}/edit");
},
function($model) {
return !$model->deleted_at;
}
], [
uctrans('texts.manage_wepay_account'),
function ($model) {
$accountGateway = AccountGateway::find($model->id);
$endpoint = WEPAY_ENVIRONMENT == WEPAY_STAGE ? 'https://stage.wepay.com/' : 'https://www.wepay.com/';
return array(
'url' => $endpoint.'account/'.$accountGateway->getConfig()->accountId,
'attributes' => 'target="_blank"'
);
},
function($model) {
return !$model->deleted_at && $model->gateway_id == GATEWAY_WEPAY;
}
]
];

View File

@ -60,11 +60,7 @@ class DatatableService
$str .= '<div class="tr-status"></div>';
}
$str .= '<div class="btn-group tr-action" style="height:auto;display:none">
<button type="button" class="btn btn-xs btn-default dropdown-toggle" data-toggle="dropdown" style="width:100px">
'.trans('texts.select').' <span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu">';
$dropdown_contents = '';
$lastIsDivider = false;
if (!$model->deleted_at || $model->deleted_at == '0000-00-00') {
@ -78,17 +74,24 @@ class DatatableService
list($value, $url, $visible) = $action;
if ($visible($model)) {
if($value == '--divider--'){
$str .= "<li class=\"divider\"></li>";
$dropdown_contents .= "<li class=\"divider\"></li>";
$lastIsDivider = true;
}
else {
$str .= "<li><a href=\"{$url($model)}\">{$value}</a></li>";
$urlVal = $url($model);
$urlStr = is_string($urlVal) ? $urlVal : $urlVal['url'];
$attributes = '';
if (!empty($urlVal['attributes'])) {
$attributes = ' '.$urlVal['attributes'];
}
$dropdown_contents .= "<li><a href=\"$urlStr\"{$attributes}>{$value}</a></li>";
$hasAction = true;
$lastIsDivider = false;
}
}
} elseif ( ! $lastIsDivider) {
$str .= "<li class=\"divider\"></li>";
$dropdown_contents .= "<li class=\"divider\"></li>";
$lastIsDivider = true;
}
}
@ -98,24 +101,35 @@ class DatatableService
}
if ( $can_edit && ! $lastIsDivider) {
$str .= "<li class=\"divider\"></li>";
$dropdown_contents .= "<li class=\"divider\"></li>";
}
if (($entityType != ENTITY_USER || $model->public_id) && $can_edit) {
$str .= "<li><a href=\"javascript:archiveEntity({$model->public_id})\">"
$dropdown_contents .= "<li><a href=\"javascript:archiveEntity({$model->public_id})\">"
. trans("texts.archive_{$entityType}") . "</a></li>";
}
} else if($can_edit) {
$str .= "<li><a href=\"javascript:restoreEntity({$model->public_id})\">"
if ($entityType != ENTITY_ACCOUNT_GATEWAY || Auth::user()->account->canAddGateway(\App\Models\Gateway::getPaymentType($model->gateway_id))) {
$dropdown_contents .= "<li><a href=\"javascript:restoreEntity({$model->public_id})\">"
. trans("texts.restore_{$entityType}") . "</a></li>";
}
}
if (property_exists($model, 'is_deleted') && !$model->is_deleted && $can_edit) {
$str .= "<li><a href=\"javascript:deleteEntity({$model->public_id})\">"
$dropdown_contents .= "<li><a href=\"javascript:deleteEntity({$model->public_id})\">"
. trans("texts.delete_{$entityType}") . "</a></li>";
}
return $str.'</ul></div></center>';
if (!empty($dropdown_contents)) {
$str .= '<div class="btn-group tr-action" style="height:auto;display:none">
<button type="button" class="btn btn-xs btn-default dropdown-toggle" data-toggle="dropdown" style="width:100px">
'.trans('texts.select').' <span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu">';
$str .= $dropdown_contents . '</ul>';
}
return $str.'</div></center>';
});
}

View File

@ -9,6 +9,7 @@ use Cache;
use Omnipay;
use Session;
use CreditCard;
use WePay;
use App\Models\Payment;
use App\Models\PaymentMethod;
use App\Models\Account;
@ -30,7 +31,8 @@ class PaymentService extends BaseService
protected static $refundableGateways = array(
GATEWAY_STRIPE,
GATEWAY_BRAINTREE
GATEWAY_BRAINTREE,
GATEWAY_WEPAY,
);
public function __construct(PaymentRepository $paymentRepo, AccountRepository $accountRepo, DatatableService $datatableService)
@ -95,9 +97,9 @@ class PaymentService extends BaseService
$data['ButtonSource'] = 'InvoiceNinja_SP';
};
if ($input && $accountGateway->isGateway(GATEWAY_STRIPE)) {
if (!empty($input['stripeToken'])) {
$data['token'] = $input['stripeToken'];
if ($input) {
if (!empty($input['sourceToken'])) {
$data['token'] = $input['sourceToken'];
unset($data['card']);
} elseif (!empty($input['plaidPublicToken'])) {
$data['plaidPublicToken'] = $input['plaidPublicToken'];
@ -106,6 +108,10 @@ class PaymentService extends BaseService
}
}
if ($accountGateway->isGateway(GATEWAY_WEPAY) && $transactionId = Session::get($invitation->id.'payment_ref')) {
$data['transaction_id'] = $transactionId;
}
return $data;
}
@ -125,7 +131,7 @@ class PaymentService extends BaseService
$data['cvv'] = $input['cvv'];
}
if (isset($input['country_id'])) {
if (isset($input['address1'])) {
$country = Country::find($input['country_id']);
$data = array_merge($data, [
@ -216,7 +222,7 @@ class PaymentService extends BaseService
public function verifyClientPaymentMethod($client, $publicId, $amount1, $amount2)
{
$token = $client->getGatewayToken($accountGateway);
$token = $client->getGatewayToken($accountGateway/* return parameter */, $accountGatewayToken/* return parameter */);
if ($accountGateway->gateway_id != GATEWAY_STRIPE) {
return 'Unsupported gateway';
}
@ -232,15 +238,18 @@ class PaymentService extends BaseService
'amounts[]=' . intval($amount1) . '&amounts[]=' . intval($amount2)
);
if (!is_string($result)) {
$paymentMethod->status = PAYMENT_METHOD_STATUS_VERIFIED;
$paymentMethod->save();
if (!$paymentMethod->account_gateway_token->default_payment_method_id) {
$paymentMethod->account_gateway_token->default_payment_method_id = $paymentMethod->id;
$paymentMethod->account_gateway_token->save();
}
if (is_string($result)) {
return $result;
}
$paymentMethod->status = PAYMENT_METHOD_STATUS_VERIFIED;
$paymentMethod->save();
if (!$paymentMethod->account_gateway_token->default_payment_method_id) {
$paymentMethod->account_gateway_token->default_payment_method_id = $paymentMethod->id;
$paymentMethod->account_gateway_token->save();
}
return true;
}
@ -266,6 +275,17 @@ class PaymentService extends BaseService
if (!$response->isSuccessful()) {
return $response->getMessage();
}
} elseif ($accountGateway->gateway_id == GATEWAY_WEPAY) {
try {
$wepay = Utils::setupWePay($accountGateway);
$wepay->request('/credit_card/delete', [
'client_id' => WEPAY_CLIENT_ID,
'client_secret' => WEPAY_CLIENT_SECRET,
'credit_card_id' => intval($paymentMethod->source_reference),
]);
} catch (\WePayException $ex){
return $ex->getMessage();
}
}
$paymentMethod->delete();
@ -291,16 +311,16 @@ class PaymentService extends BaseService
{
$customerReference = $client->getGatewayToken($accountGateway, $accountGatewayToken/* return paramenter */);
if ($customerReference) {
if ($customerReference && $customerReference != CUSTOMER_REFERENCE_LOCAL) {
$details['customerReference'] = $customerReference;
if ($accountGateway->gateway->id == GATEWAY_STRIPE) {
if ($accountGateway->gateway_id == GATEWAY_STRIPE) {
$customerResponse = $gateway->fetchCustomer(array('customerReference' => $customerReference))->send();
if (!$customerResponse->isSuccessful()) {
$customerReference = null; // The customer might not exist anymore
}
} elseif ($accountGateway->gateway->id == GATEWAY_BRAINTREE) {
} elseif ($accountGateway->gateway_id == GATEWAY_BRAINTREE) {
$customer = $gateway->findCustomer($customerReference)->send()->getData();
if (!($customer instanceof \Braintree\Customer)) {
@ -309,7 +329,7 @@ class PaymentService extends BaseService
}
}
if ($accountGateway->gateway->id == GATEWAY_STRIPE) {
if ($accountGateway->gateway_id == GATEWAY_STRIPE) {
if (!empty($details['plaidPublicToken'])) {
$plaidResult = $this->getPlaidToken($accountGateway, $details['plaidPublicToken'], $details['plaidAccountId']);
@ -355,7 +375,7 @@ class PaymentService extends BaseService
return;
}
}
} elseif ($accountGateway->gateway->id == GATEWAY_BRAINTREE) {
} elseif ($accountGateway->gateway_id == GATEWAY_BRAINTREE) {
if (!$customerReference) {
$tokenResponse = $gateway->createCustomer(array('customerData' => array()))->send();
if ($tokenResponse->isSuccessful()) {
@ -377,6 +397,38 @@ class PaymentService extends BaseService
return;
}
}
} elseif ($accountGateway->gateway_id == GATEWAY_WEPAY) {
$wepay = Utils::setupWePay($accountGateway);
try {
$wepay->request('credit_card/authorize', array(
'client_id' => WEPAY_CLIENT_ID,
'client_secret' => WEPAY_CLIENT_SECRET,
'credit_card_id' => intval($details['token']),
));
// Update the callback uri and get the card details
$wepay->request('credit_card/modify', array(
'client_id' => WEPAY_CLIENT_ID,
'client_secret' => WEPAY_CLIENT_SECRET,
'credit_card_id' => intval($details['token']),
'auto_update' => WEPAY_AUTO_UPDATE,
'callback_uri' => URL::to(env('WEBHOOK_PREFIX','').'paymenthook/'.$client->account->account_key.'/'.GATEWAY_WEPAY),
));
$tokenResponse = $wepay->request('credit_card', array(
'client_id' => WEPAY_CLIENT_ID,
'client_secret' => WEPAY_CLIENT_SECRET,
'credit_card_id' => intval($details['token']),
));
$customerReference = CUSTOMER_REFERENCE_LOCAL;
$sourceReference = $details['token'];
} catch (\WePayException $ex) {
$this->lastError = $ex->getMessage();
return;
}
} else {
return null;
}
if ($customerReference) {
@ -394,7 +446,7 @@ class PaymentService extends BaseService
$accountGatewayToken->token = $customerReference;
$accountGatewayToken->save();
$paymentMethod = $this->createPaymentMethodFromGatewayResponse($tokenResponse, $accountGateway, $accountGatewayToken, $contactId);
$paymentMethod = $this->convertPaymentMethodFromGatewayResponse($tokenResponse, $accountGateway, $accountGatewayToken, $contactId);
} else {
$this->lastError = $tokenResponse->getMessage();
@ -422,7 +474,7 @@ class PaymentService extends BaseService
$paymentMethod->setRelation('currency', $currency);
}
} elseif ($source['object'] == 'card') {
$paymentMethod->expiration = $source['exp_year'] . '-' . $source['exp_month'] . '-00';
$paymentMethod->expiration = $source['exp_year'] . '-' . $source['exp_month'] . '-01';
$paymentMethod->payment_type_id = $this->parseCardType($source['brand']);
} else {
return null;
@ -442,7 +494,7 @@ class PaymentService extends BaseService
if ($source instanceof \Braintree\CreditCard) {
$paymentMethod->payment_type_id = $this->parseCardType($source->cardType);
$paymentMethod->last4 = $source->last4;
$paymentMethod->expiration = $source->expirationYear . '-' . $source->expirationMonth . '-00';
$paymentMethod->expiration = $source->expirationYear . '-' . $source->expirationMonth . '-01';
} elseif ($source instanceof \Braintree\PayPalAccount) {
$paymentMethod->email = $source->email;
$paymentMethod->payment_type_id = PAYMENT_TYPE_ID_PAYPAL;
@ -456,8 +508,24 @@ class PaymentService extends BaseService
return $paymentMethod;
}
public function convertPaymentMethodFromWePay($source, $accountGatewayToken = null, $paymentMethod = null) {
// Creating a new one or updating an existing one
if (!$paymentMethod) {
$paymentMethod = $accountGatewayToken ? PaymentMethod::createNew($accountGatewayToken) : new PaymentMethod();
}
$paymentMethod->payment_type_id = $this->parseCardType($source->credit_card_name);
$paymentMethod->last4 = $source->last_four;
$paymentMethod->expiration = $source->expiration_year . '-' . $source->expiration_month . '-01';
$paymentMethod->setRelation('payment_type', Cache::get('paymentTypes')->find($paymentMethod->payment_type_id));
$paymentMethod->source_reference = $source->credit_card_id;
return $paymentMethod;
}
public function createPaymentMethodFromGatewayResponse($gatewayResponse, $accountGateway, $accountGatewayToken = null, $contactId = null) {
public function convertPaymentMethodFromGatewayResponse($gatewayResponse, $accountGateway, $accountGatewayToken = null, $contactId = null, $existingPaymentMethod = null) {
if ($accountGateway->gateway_id == GATEWAY_STRIPE) {
$data = $gatewayResponse->getData();
if (!empty($data['object']) && ($data['object'] == 'card' || $data['object'] == 'bank_account')) {
@ -470,7 +538,7 @@ class PaymentService extends BaseService
}
if ($source) {
$paymentMethod = $this->convertPaymentMethodFromStripe($source, $accountGatewayToken);
$paymentMethod = $this->convertPaymentMethodFromStripe($source, $accountGatewayToken, $existingPaymentMethod);
}
} elseif ($accountGateway->gateway_id == GATEWAY_BRAINTREE) {
$data = $gatewayResponse->getData();
@ -478,11 +546,16 @@ class PaymentService extends BaseService
if (!empty($data->transaction)) {
$transaction = $data->transaction;
$paymentMethod = $accountGatewayToken ? PaymentMethod::createNew($accountGatewayToken) : new PaymentMethod();
if ($existingPaymentMethod) {
$paymentMethod = $existingPaymentMethod;
} else {
$paymentMethod = $accountGatewayToken ? PaymentMethod::createNew($accountGatewayToken) : new PaymentMethod();
}
if ($transaction->paymentInstrumentType == 'credit_card') {
$card = $transaction->creditCardDetails;
$paymentMethod->last4 = $card->last4;
$paymentMethod->expiration = $card->expirationYear . '-' . $card->expirationMonth . '-00';
$paymentMethod->expiration = $card->expirationYear . '-' . $card->expirationMonth . '-01';
$paymentMethod->payment_type_id = $this->parseCardType($card->cardType);
} elseif ($transaction->paymentInstrumentType == 'paypal_account') {
$paymentMethod->payment_type_id = PAYMENT_TYPE_ID_PAYPAL;
@ -490,9 +563,20 @@ class PaymentService extends BaseService
}
$paymentMethod->setRelation('payment_type', Cache::get('paymentTypes')->find($paymentMethod->payment_type_id));
} elseif (!empty($data->paymentMethod)) {
$paymentMethod = $this->convertPaymentMethodFromBraintree($data->paymentMethod, $accountGatewayToken);
$paymentMethod = $this->convertPaymentMethodFromBraintree($data->paymentMethod, $accountGatewayToken, $existingPaymentMethod);
}
} elseif ($accountGateway->gateway_id == GATEWAY_WEPAY) {
if ($gatewayResponse instanceof \Omnipay\WePay\Message\CustomCheckoutResponse) {
$wepay = \Utils::setupWePay($accountGateway);
$gatewayResponse = $wepay->request('credit_card', array(
'client_id' => WEPAY_CLIENT_ID,
'client_secret' => WEPAY_CLIENT_SECRET,
'credit_card_id' => $gatewayResponse->getData()['payment_method']['credit_card']['id'],
));
}
$paymentMethod = $this->convertPaymentMethodFromWePay($gatewayResponse, $accountGatewayToken, $existingPaymentMethod);
}
if (!empty($paymentMethod) && $accountGatewayToken && $contactId) {
@ -566,43 +650,49 @@ class PaymentService extends BaseService
$payment->payment_type_id = $this->detectCardType($card->getNumber());
}
$savePaymentMethod = !empty($paymentMethod);
// This will convert various gateway's formats to a known format
$paymentMethod = $this->convertPaymentMethodFromGatewayResponse($purchaseResponse, $accountGateway, null, null, $paymentMethod);
// If this is a stored payment method, we'll update it with the latest info
if ($savePaymentMethod) {
$paymentMethod->save();
}
if ($accountGateway->gateway_id == GATEWAY_STRIPE) {
$data = $purchaseResponse->getData();
$source = !empty($data['source'])?$data['source']:$data['card'];
$payment->payment_status_id = $data['status'] == 'succeeded' ? PAYMENT_STATUS_COMPLETED : PAYMENT_STATUS_PENDING;
if ($source) {
$payment->last4 = $source['last4'];
if ($source['object'] == 'bank_account') {
$payment->routing_number = $source['routing_number'];
$payment->payment_type_id = PAYMENT_TYPE_ACH;
}
else{
$payment->expiration = $source['exp_year'] . '-' . $source['exp_month'] . '-00';
$payment->payment_type_id = $this->parseCardType($source['brand']);
}
}
} elseif ($accountGateway->gateway_id == GATEWAY_BRAINTREE) {
$transaction = $purchaseResponse->getData()->transaction;
if ($transaction->paymentInstrumentType == 'credit_card') {
$card = $transaction->creditCardDetails;
$payment->last4 = $card->last4;
$payment->expiration = $card->expirationYear . '-' . $card->expirationMonth . '-00';
$payment->payment_type_id = $this->parseCardType($card->cardType);
} elseif ($transaction->paymentInstrumentType == 'paypal_account') {
$payment->payment_type_id = PAYMENT_TYPE_ID_PAYPAL;
$payment->email = $transaction->paypalDetails->payerEmail;
}
}
if ($payerId) {
$payment->payer_id = $payerId;
}
if ($paymentMethod) {
$payment->payment_method_id = $paymentMethod->id;
if ($paymentMethod->last4) {
$payment->last4 = $paymentMethod->last4;
}
if ($paymentMethod->expiration) {
$payment->expiration = $paymentMethod->expiration;
}
if ($paymentMethod->routing_number) {
$payment->routing_number = $paymentMethod->routing_number;
}
if ($paymentMethod->payment_type_id) {
$payment->payment_type_id = $paymentMethod->payment_type_id;
}
if ($paymentMethod->email) {
$payment->email = $paymentMethod->email;
}
if ($payerId) {
$payment->payer_id = $payerId;
}
if ($savePaymentMethod) {
$payment->payment_method_id = $paymentMethod->id;
}
}
$payment->save();
@ -665,20 +755,29 @@ class PaymentService extends BaseService
private function parseCardType($cardName) {
$cardTypes = array(
'Visa' => PAYMENT_TYPE_VISA,
'American Express' => PAYMENT_TYPE_AMERICAN_EXPRESS,
'MasterCard' => PAYMENT_TYPE_MASTERCARD,
'Discover' => PAYMENT_TYPE_DISCOVER,
'JCB' => PAYMENT_TYPE_JCB,
'Diners Club' => PAYMENT_TYPE_DINERS,
'Carte Blanche' => PAYMENT_TYPE_CARTE_BLANCHE,
'China UnionPay' => PAYMENT_TYPE_UNIONPAY,
'Laser' => PAYMENT_TYPE_LASER,
'Maestro' => PAYMENT_TYPE_MAESTRO,
'Solo' => PAYMENT_TYPE_SOLO,
'Switch' => PAYMENT_TYPE_SWITCH
'visa' => PAYMENT_TYPE_VISA,
'americanexpress' => PAYMENT_TYPE_AMERICAN_EXPRESS,
'amex' => PAYMENT_TYPE_AMERICAN_EXPRESS,
'mastercard' => PAYMENT_TYPE_MASTERCARD,
'discover' => PAYMENT_TYPE_DISCOVER,
'jcb' => PAYMENT_TYPE_JCB,
'dinersclub' => PAYMENT_TYPE_DINERS,
'carteblanche' => PAYMENT_TYPE_CARTE_BLANCHE,
'chinaunionpay' => PAYMENT_TYPE_UNIONPAY,
'unionpay' => PAYMENT_TYPE_UNIONPAY,
'laser' => PAYMENT_TYPE_LASER,
'maestro' => PAYMENT_TYPE_MAESTRO,
'solo' => PAYMENT_TYPE_SOLO,
'switch' => PAYMENT_TYPE_SWITCH
);
$cardName = strtolower(str_replace(array(' ', '-', '_'), '', $cardName));
if (empty($cardTypes[$cardName]) && 1 == preg_match('/^('.implode('|', array_keys($cardTypes)).')/', $cardName, $matches)) {
// Some gateways return extra stuff after the card name
$cardName = $matches[1];
}
if (!empty($cardTypes[$cardName])) {
return $cardTypes[$cardName];
} else {
@ -725,6 +824,11 @@ class PaymentService extends BaseService
$invitation = $invoice->invitations->first();
$token = $client->getGatewayToken($accountGateway/* return parameter */, $accountGatewayToken/* return parameter */);
if (!$accountGatewayToken) {
return false;
}
$defaultPaymentMethod = $accountGatewayToken->default_payment_method;
if (!$invitation || !$token || !$defaultPaymentMethod) {
@ -736,10 +840,9 @@ class PaymentService extends BaseService
$details = $this->getPaymentDetails($invitation, $accountGateway);
$details['customerReference'] = $token;
if ($accountGateway->gateway_id == GATEWAY_STRIPE) {
$details['cardReference'] = $defaultPaymentMethod->source_reference;
} elseif ($accountGateway->gateway_id == GATEWAY_BRAINTREE) {
$details['paymentMethodToken'] = $defaultPaymentMethod->source_reference;
$details['token'] = $defaultPaymentMethod->source_reference;
if ($accountGateway->gateway_id == GATEWAY_WEPAY) {
$details['transaction_id'] = 'autobill_'.$invoice->id;
}
// submit purchase/get response
@ -948,41 +1051,76 @@ class PaymentService extends BaseService
if ($payment->payment_type_id != PAYMENT_TYPE_CREDIT) {
$gateway = $this->createGateway($accountGateway);
$refund = $gateway->refund(array(
'transactionReference' => $payment->transaction_reference,
'amount' => $amount,
));
$response = $refund->send();
if ($response->isSuccessful()) {
$payment->recordRefund($amount);
if ($accountGateway->gateway_id != GATEWAY_WEPAY) {
$refund = $gateway->refund(array(
'transactionReference' => $payment->transaction_reference,
'amount' => $amount,
));
$response = $refund->send();
if ($response->isSuccessful()) {
$payment->recordRefund($amount);
} else {
$data = $response->getData();
if ($data instanceof \Braintree\Result\Error) {
$error = $data->errors->deepAll()[0];
if ($error && $error->code == 91506) {
if ($amount == $payment->amount) {
// This is an unsettled transaction; try to void it
$void = $gateway->void(array(
'transactionReference' => $payment->transaction_reference,
));
$response = $void->send();
if ($response->isSuccessful()) {
$payment->markVoided();
}
} else {
$this->error('Unknown', 'Partial refund not allowed for unsettled transactions.', $accountGateway);
return false;
}
}
}
if (!$response->isSuccessful()) {
$this->error('Unknown', $response->getMessage(), $accountGateway);
return false;
}
}
} else {
$data = $response->getData();
$wepay = \Utils::setupWePay($accountGateway);
if ($data instanceof \Braintree\Result\Error) {
$error = $data->errors->deepAll()[0];
if ($error && $error->code == 91506) {
try {
$wepay->request('checkout/refund', array(
'checkout_id' => intval($payment->transaction_reference),
'refund_reason' => 'Refund issued by merchant.',
'amount' => $amount,
));
$payment->recordRefund($amount);
} catch (\WePayException $ex) {
if ($ex->getCode() == 4004) {
if ($amount == $payment->amount) {
// This is an unsettled transaction; try to void it
$void = $gateway->void(array(
'transactionReference' => $payment->transaction_reference,
));
$response = $void->send();
if ($response->isSuccessful()) {
try {
// This is an uncaptured transaction; try to cancel it
$wepay->request('checkout/cancel', array(
'checkout_id' => intval($payment->transaction_reference),
'cancel_reason' => 'Refund issued by merchant.',
));
$payment->markVoided();
} catch (\WePayException $ex) {
$this->error('Unknown', $ex->getMessage(), $accountGateway);
}
} else {
$this->error('Unknown', 'Partial refund not allowed for unsettled transactions.', $accountGateway);
return false;
}
} else {
$this->error('Unknown', $ex->getMessage(), $accountGateway);
}
}
if (!$response->isSuccessful()) {
$this->error('Unknown', $response->getMessage(), $accountGateway);
return false;
}
}
} else {
$payment->recordRefund($amount);

View File

@ -74,7 +74,8 @@
"league/flysystem-rackspace": "~1.0",
"barracudanetworks/archivestream-php": "^1.0",
"omnipay/braintree": "~2.0@dev",
"gatepay/FedACHdir": "dev-master@dev"
"gatepay/FedACHdir": "dev-master@dev",
"wepay/php-sdk": "^0.2"
},
"require-dev": {
"phpunit/phpunit": "~4.0",

57
composer.lock generated
View File

@ -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": "7139e4aedb2ac151079c50ee5c17f93c",
"content-hash": "a314d6c0a16785dd2395a7fd73cdc76d",
"hash": "b2471aea1af5ef67a1379ad95b5138f7",
"content-hash": "df30a311df0341933d4ff2c3aa5974a6",
"packages": [
{
"name": "agmscode/omnipay-agms",
@ -505,7 +505,7 @@
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/e97ed532f09e290b91ff7713b785ed7ab11d0812",
"url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/f7b31bdbdceaaea930c71df20e4180b0b7172b4a",
"reference": "e97ed532f09e290b91ff7713b785ed7ab11d0812",
"shasum": ""
},
@ -2785,7 +2785,7 @@
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/labs7in0/omnipay-wechat/zipball/40c9f86df6573ad98ae1dd0d29712ccbc789a74e",
"url": "https://api.github.com/repos/labs7in0/omnipay-wechat/zipball/c8d80c3b48bae2bab071f283f75b1cd8624ed3c7",
"reference": "40c9f86df6573ad98ae1dd0d29712ccbc789a74e",
"shasum": ""
},
@ -8057,6 +8057,53 @@
],
"time": "2016-02-25 10:29:59"
},
{
"name": "wepay/php-sdk",
"version": "0.2.7",
"source": {
"type": "git",
"url": "https://github.com/wepay/PHP-SDK.git",
"reference": "31bfcdd97d2c9c33c9c09129638ae31840822182"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/wepay/PHP-SDK/zipball/31bfcdd97d2c9c33c9c09129638ae31840822182",
"reference": "31bfcdd97d2c9c33c9c09129638ae31840822182",
"shasum": ""
},
"require": {
"ext-curl": "*",
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "0.2.x-dev"
}
},
"autoload": {
"classmap": [
"wepay.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "WePay",
"email": "api@wepay.com"
}
],
"description": "WePay APIv2 SDK for PHP",
"keywords": [
"payment",
"sdk",
"wepay"
],
"time": "2015-08-14 19:42:37"
},
{
"name": "wildbit/laravel-postmark-provider",
"version": "3.0.0",
@ -10024,4 +10071,4 @@
"prefer-lowest": false,
"platform": [],
"platform-dev": []
}
}

View File

@ -78,6 +78,11 @@ class PaymentsChanges extends Migration
->where('auto_bill', '=', 1)
->update(array('client_enable_auto_bill' => 1, 'auto_bill' => AUTO_BILL_OPT_OUT));
\DB::table('invoices')
->where('auto_bill', '=', 0)
->where('is_recurring', '=', 1)
->update(array('auto_bill' => AUTO_BILL_OFF));
Schema::table('account_gateway_tokens', function($table)
{
@ -113,11 +118,15 @@ class PaymentsChanges extends Migration
$table->dropColumn('payment_method_id');
});
\DB::table('invoices')
->where('auto_bill', '=', AUTO_BILL_OFF)
->update(array('auto_bill' => 0));
\DB::table('invoices')
->where(function($query){
$query->where('auto_bill', '=', AUTO_BILL_ALWAYS);
$query->orwhere(function($query){
$query->where('auto_bill', '!=', AUTO_BILL_OFF);
$query->where('auto_bill', '!=', 0);
$query->where('client_enable_auto_bill', '=', 1);
});
})

View File

@ -30933,7 +30933,7 @@ function truncate(string, length){
// Show/hide the 'Select' option in the datalists
function actionListHandler() {
$('tbody tr').mouseover(function() {
$('tbody tr .tr-action').closest('tr').mouseover(function() {
$(this).closest('tr').find('.tr-action').show();
$(this).closest('tr').find('.tr-status').hide();
}).mouseout(function() {

View File

@ -1042,7 +1042,7 @@ function truncate(string, length){
// Show/hide the 'Select' option in the datalists
function actionListHandler() {
$('tbody tr').mouseover(function() {
$('tbody tr .tr-action').closest('tr').mouseover(function() {
$(this).closest('tr').find('.tr-action').show();
$(this).closest('tr').find('.tr-status').hide();
}).mouseout(function() {

View File

@ -1282,6 +1282,26 @@ $LANG = array(
'import_ofx' => 'Import OFX',
'ofx_file' => 'OFX File',
'ofx_parse_failed' => 'Failed to parse OFX file',
// WePay
'wepay' => 'WePay',
'sign_up_with_wepay' => 'Sign up with WePay',
'use_another_provider' => 'Use another provider',
'company_name' => 'Company Name',
'wepay_company_name_help' => 'This will appear on client\'s credit card statements.',
'wepay_description_help' => 'The purpose of this account.',
'wepay_tos_agree' => 'I agree to the :link.',
'wepay_tos_link_text' => 'WePay Terms of Service',
'resend_confirmation_email' => 'Resend Confirmation Email',
'manage_wepay_account' => 'Manage WePay Account',
'action_required' => 'Action Required',
'finish_setup' => 'Finish Setup',
'created_wepay_confirmation_required' => 'Please check your email and confirm your email address with WePay.',
'switch_to_wepay' => 'Switch to WePay',
'switch' => 'Switch',
'restore_account_gateway' => 'Restore Gateway',
'restored_account_gateway' => 'Successfully restored gateway',
);
return $LANG;

View File

@ -5,15 +5,18 @@
@include('accounts.nav', ['selected' => ACCOUNT_PAYMENTS])
{!! Former::open($url)->method($method)->rule()->addClass('warn-on-exit') !!}
{!! Former::populateField('token_billing_type_id', $account->token_billing_type_id) !!}
@if(!$accountGateway && WEPAY_CLIENT_ID && !$account->getGatewayByType(PAYMENT_TYPE_CREDIT_CARD) && !$account->getGatewayByType(PAYMENT_TYPE_STRIPE))
@include('accounts.partials.account_gateway_wepay')
@endif
<div id="other-providers">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{!! trans($title) !!}</h3>
</div>
<div class="panel-body form-padding-right">
{!! Former::open($url)->method($method)->rule()->addClass('warn-on-exit') !!}
{!! Former::populateField('token_billing_type_id', $account->token_billing_type_id) !!}
@if ($accountGateway)
{!! Former::populateField('gateway_id', $accountGateway->gateway_id) !!}
@ -91,7 +94,7 @@
{!! Former::text('publishable_key') !!}
@endif
@if ($gateway->id == GATEWAY_STRIPE || $gateway->id == GATEWAY_BRAINTREE)
@if ($gateway->id == GATEWAY_STRIPE || $gateway->id == GATEWAY_BRAINTREE || $gateway->id == GATEWAY_WEPAY)
{!! Former::select('token_billing_type_id')
->options($tokenBillingOptions)
->help(trans('texts.token_billing_help')) !!}
@ -101,7 +104,7 @@
<div class="form-group">
<label class="control-label col-lg-4 col-sm-4">{{ trans('texts.webhook_url') }}</label>
<div class="col-lg-8 col-sm-8 help-block">
<input type="text" class="form-control" onfocus="$(this).select()" readonly value="{{ URL::to('/paymenthook/'.$account->account_key.'/'.GATEWAY_STRIPE) }}">
<input type="text" class="form-control" onfocus="$(this).select()" readonly value="{{ URL::to(env('WEBHOOK_PREFIX','').'paymenthook/'.$account->account_key.'/'.GATEWAY_STRIPE) }}">
<div class="help-block"><strong>{!! trans('texts.stripe_webhook_help', [
'link'=>'<a href="https://dashboard.stripe.com/account/webhooks" target="_blank">'.trans('texts.stripe_webhook_help_link_text').'</a>'
]) !!}</strong></div>
@ -173,7 +176,6 @@
</div>
@endif
</div>
</div>
</div>
@ -183,6 +185,7 @@
$countGateways > 0 ? Button::normal(trans('texts.cancel'))->large()->asLinkTo(URL::to('/settings/online_payments'))->appendIcon(Icon::create('remove-circle')) : false,
Button::success(trans('texts.save'))->submit()->large()->appendIcon(Icon::create('floppy-disk'))) !!}
{!! Former::close() !!}
</div>
<script type="text/javascript">
@ -256,6 +259,11 @@
enableUpdateAddress();
$('#enable_ach').change(enablePlaidSettings)
$('#show-other-providers').click(function(){
$(this).hide();
$('#other-providers').show();
})
})
</script>

View File

@ -0,0 +1,7 @@
@extends('header')
@section('content')
@parent
@include('accounts.nav', ['selected' => ACCOUNT_PAYMENTS])
@include('accounts.partials.account_gateway_wepay')
@stop

View File

@ -0,0 +1,59 @@
{!! Former::open($url)->method($method)->rules(array(
'first_name' => 'required',
'last_name' => 'required',
'email' => 'required',
'description' => 'required',
'company_name' => 'required',
'tos_agree' => 'required',
))->addClass('warn-on-exit') !!}
{!! Former::populateField('company_name', $account->getDisplayName()) !!}
{!! Former::populateField('first_name', $user->first_name) !!}
{!! Former::populateField('last_name', $user->last_name) !!}
{!! Former::populateField('email', $user->email) !!}
{!! Former::populateField('show_address', 1) !!}
{!! Former::populateField('update_address', 1) !!}
{!! Former::populateField('token_billing_type_id', $account->token_billing_type_id) !!}
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{!! trans('texts.online_payments') !!}</h3>
</div>
<div class="panel-body form-padding-right">
{!! Former::text('first_name') !!}
{!! Former::text('last_name') !!}
{!! Former::text('email') !!}
{!! Former::text('company_name')->help('wepay_company_name_help')->maxlength(255) !!}
{!! Former::text('description')->help('wepay_description_help') !!}
{!! Former::select('token_billing_type_id')
->options($tokenBillingOptions)
->help(trans('texts.token_billing_help')) !!}
{!! Former::checkbox('show_address')
->label(trans('texts.billing_address'))
->text(trans('texts.show_address_help'))
->addGroupClass('gateway-option') !!}
{!! Former::checkbox('update_address')
->label(' ')
->text(trans('texts.update_address_help'))
->addGroupClass('gateway-option') !!}
{!! Former::checkboxes('creditCardTypes[]')
->label('Accepted Credit Cards')
->checkboxes($creditCardTypes)
->class('creditcard-types')
->addGroupClass('gateway-option')
!!}
{!! Former::checkbox('tos_agree')->label(' ')->text(trans('texts.wepay_tos_agree',
['link'=>'<a href="https://go.wepay.com/terms-of-service-us" target="_blank">'.trans('texts.wepay_tos_link_text').'</a>']
))->value('true') !!}
<center>
{!! Button::primary(trans('texts.sign_up_with_wepay'))
->submit()
->large() !!}
@if(isset($gateways))
<br><br>
<a href="javascript::void" id="show-other-providers">{{ trans('texts.use_another_provider') }}</a>
@endif
</center>
</div>
</div>
<style>#other-providers{display:none}</style>
<input type="hidden" name="gateway_id" value="{{ GATEWAY_WEPAY }}">
{!! Former::close() !!}

View File

@ -4,6 +4,17 @@
@parent
@include('accounts.nav', ['selected' => ACCOUNT_PAYMENTS])
@if ($showSwitchToWepay)
{!! Button::success(trans('texts.switch_to_wepay'))
->asLinkTo(URL::to('/gateways/switch/wepay'))
->appendIcon(Icon::create('circle-arrow-up')) !!}
&nbsp;
@endif
<label for="trashed" style="font-weight:normal; margin-left: 10px;">
<input id="trashed" type="checkbox" onclick="setTrashVisible()"
{{ Session::get("show_trash:gateway") ? 'checked' : ''}}/>&nbsp; {{ trans('texts.show_archived_deleted')}} {{ Utils::transFlowText('gateways') }}
</label>
@if ($showAdd)
{!! Button::primary(trans('texts.add_gateway'))
->asLinkTo(URL::to('/gateways/create'))
@ -28,6 +39,14 @@
<script>
window.onDatatableReady = actionListHandler;
function setTrashVisible() {
var checked = $('#trashed').is(':checked');
var url = '{{ URL::to('view_archive/gateway') }}' + (checked ? '/true' : '/false');
$.get(url, function(data) {
refreshDatatable();
})
}
</script>
@stop

View File

@ -166,24 +166,24 @@
</span>
@if($account->getTokenGatewayId())
<span data-bind="visible: is_recurring()" style="display: none">
<div data-bind="visible: !(auto_bill() == 1 &amp;&amp; client_enable_auto_bill()) &amp;&amp; !(auto_bill() == 2 &amp;&amp; !client_enable_auto_bill())" style="display: none">
<div data-bind="visible: !(auto_bill() == {{AUTO_BILL_OPT_IN}} &amp;&amp; client_enable_auto_bill()) &amp;&amp; !(auto_bill() == {{AUTO_BILL_OPT_OUT}} &amp;&amp; !client_enable_auto_bill())" style="display: none">
{!! Former::select('auto_bill')
->data_bind("value: auto_bill, valueUpdate: 'afterkeydown', event:{change:function(){if(auto_bill()==1)client_enable_auto_bill(0);if(auto_bill()==2)client_enable_auto_bill(1)}}")
->data_bind("value: auto_bill, valueUpdate: 'afterkeydown', event:{change:function(){if(auto_bill()==".AUTO_BILL_OPT_IN.")client_enable_auto_bill(0);if(auto_bill()==".AUTO_BILL_OPT_OUT.")client_enable_auto_bill(1)}}")
->options([
0 => trans('texts.off'),
1 => trans('texts.opt_in'),
2 => trans('texts.opt_out'),
3 => trans('texts.always'),
AUTO_BILL_OFF => trans('texts.off'),
AUTO_BILL_OPT_IN => trans('texts.opt_in'),
AUTO_BILL_OPT_OUT => trans('texts.opt_out'),
AUTO_BILL_ALWAYS => trans('texts.always'),
]) !!}
</div>
<input type="hidden" name="client_enable_auto_bill" data-bind="attr: { value: client_enable_auto_bill() }" />
<div class="form-group" data-bind="visible: auto_bill() == 1 &amp;&amp; client_enable_auto_bill()">
<div class="form-group" data-bind="visible: auto_bill() == {{AUTO_BILL_OPT_IN}} &amp;&amp; client_enable_auto_bill()">
<div class="col-sm-4 control-label">{{trans('texts.auto_bill')}}</div>
<div class="col-sm-8" style="padding-top:10px;padding-bottom:9px">
{{trans('texts.opted_in')}} - <a href="#" data-bind="click:function(){client_enable_auto_bill(false)}">({{trans('texts.disable')}})</a>
</div>
</div>
<div class="form-group" data-bind="visible: auto_bill() == 2 &amp;&amp; !client_enable_auto_bill()">
<div class="form-group" data-bind="visible: auto_bill() == {{AUTO_BILL_OPT_OUT}} &amp;&amp; !client_enable_auto_bill()">
<div class="col-sm-4 control-label">{{trans('texts.auto_bill')}}</div>
<div class="col-sm-8" style="padding-top:10px;padding-bottom:9px">
{{trans('texts.opted_out')}} - <a href="#" data-bind="click:function(){client_enable_auto_bill(true)}">({{trans('texts.enable')}})</a>

View File

@ -3,221 +3,11 @@
@section('head')
@parent
@if (!empty($braintreeClientToken))
<script type="text/javascript" src="https://js.braintreegateway.com/js/braintree-2.23.0.min.js"></script>
<script type="text/javascript" >
$(function() {
braintree.setup("{{ $braintreeClientToken }}", "custom", {
id: "payment-form",
hostedFields: {
number: {
selector: "#card_number",
placeholder: "{{ trans('texts.card_number') }}"
},
cvv: {
selector: "#cvv",
placeholder: "{{ trans('texts.cvv') }}"
},
expirationMonth: {
selector: "#expiration_month",
placeholder: "{{ trans('texts.expiration_month') }}"
},
expirationYear: {
selector: "#expiration_year",
placeholder: "{{ trans('texts.expiration_year') }}"
},
styles: {
'input': {
'font-family': {!! json_encode(Utils::getFromCache($account->getBodyFontId(), 'fonts')['css_stack']) !!},
'font-weight': "{{ Utils::getFromCache($account->getBodyFontId(), 'fonts')['css_weight'] }}",
'font-size': '16px'
}
}
},
onError: function(e) {
var $form = $('.payment-form');
$form.find('button').prop('disabled', false);
// Show the errors on the form
if (e.details && e.details.invalidFieldKeys.length) {
var invalidField = e.details.invalidFieldKeys[0];
if (invalidField == 'number') {
$('#js-error-message').html('{{ trans('texts.invalid_card_number') }}').fadeIn();
}
else if (invalidField == 'expirationDate' || invalidField == 'expirationYear' || invalidField == 'expirationMonth') {
$('#js-error-message').html('{{ trans('texts.invalid_expiry') }}').fadeIn();
}
else if (invalidField == 'cvv') {
$('#js-error-message').html('{{ trans('texts.invalid_cvv') }}').fadeIn();
}
}
else {
$('#js-error-message').html(e.message).fadeIn();
}
}
});
$('.payment-form').submit(function(event) {
var $form = $(this);
// Disable the submit button to prevent repeated clicks
$form.find('button').prop('disabled', true);
$('#js-error-message').hide();
});
});
</script>
@include('payments.tokenization_braintree')
@elseif (isset($accountGateway) && $accountGateway->getPublishableStripeKey())
<script type="text/javascript" src="https://js.stripe.com/v2/"></script>
<script type="text/javascript">
Stripe.setPublishableKey('{{ $accountGateway->getPublishableStripeKey() }}');
$(function() {
$('.payment-form').submit(function(event) {
if($('[name=plaidAccountId]').length)return;
var $form = $(this);
var data = {
@if($paymentType == PAYMENT_TYPE_STRIPE_ACH)
account_holder_name: $('#account_holder_name').val(),
account_holder_type: $('[name=account_holder_type]:checked').val(),
currency: $("#currency").val(),
country: $("#country").val(),
routing_number: $('#routing_number').val().replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ''),
account_number: $('#account_number').val().replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '')
@else
name: $('#first_name').val() + ' ' + $('#last_name').val(),
address_line1: $('#address1').val(),
address_line2: $('#address2').val(),
address_city: $('#city').val(),
address_state: $('#state').val(),
address_zip: $('#postal_code').val(),
address_country: $("#country_id option:selected").text(),
number: $('#card_number').val(),
cvc: $('#cvv').val(),
exp_month: $('#expiration_month').val(),
exp_year: $('#expiration_year').val()
@endif
};
@if($paymentType == PAYMENT_TYPE_STRIPE_ACH)
// Validate the account details
if (!data.account_holder_type) {
$('#js-error-message').html('{{ trans('texts.missing_account_holder_type') }}').fadeIn();
return false;
}
if (!data.account_holder_name) {
$('#js-error-message').html('{{ trans('texts.missing_account_holder_name') }}').fadeIn();
return false;
}
if (!data.routing_number || !Stripe.bankAccount.validateRoutingNumber(data.routing_number, data.country)) {
$('#js-error-message').html('{{ trans('texts.invalid_routing_number') }}').fadeIn();
return false;
}
if (data.account_number != $('#confirm_account_number').val().replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '')) {
$('#js-error-message').html('{{ trans('texts.account_number_mismatch') }}').fadeIn();
return false;
}
if (!data.account_number || !Stripe.bankAccount.validateAccountNumber(data.account_number, data.country)) {
$('#js-error-message').html('{{ trans('texts.invalid_account_number') }}').fadeIn();
return false;
}
@else
// Validate the card details
if (!Stripe.card.validateCardNumber(data.number)) {
$('#js-error-message').html('{{ trans('texts.invalid_card_number') }}').fadeIn();
return false;
}
if (!Stripe.card.validateExpiry(data.exp_month, data.exp_year)) {
$('#js-error-message').html('{{ trans('texts.invalid_expiry') }}').fadeIn();
return false;
}
if (!Stripe.card.validateCVC(data.cvc)) {
$('#js-error-message').html('{{ trans('texts.invalid_cvv') }}').fadeIn();
return false;
}
@endif
// Disable the submit button to prevent repeated clicks
$form.find('button').prop('disabled', true);
$('#js-error-message').hide();
@if($paymentType == PAYMENT_TYPE_STRIPE_ACH)
Stripe.bankAccount.createToken(data, stripeResponseHandler);
@else
Stripe.card.createToken(data, stripeResponseHandler);
@endif
// Prevent the form from submitting with the default action
return false;
});
@if($accountGateway->getPlaidEnabled())
var plaidHandler = Plaid.create({
selectAccount: true,
env: '{{ $accountGateway->getPlaidEnvironment() }}',
clientName: {!! json_encode($account->getDisplayName()) !!},
key: '{{ $accountGateway->getPlaidPublicKey() }}',
product: 'auth',
onSuccess: plaidSuccessHandler,
onExit : function(){$('#secured_by_plaid').hide()}
});
$('#plaid_link_button').click(function(){plaidHandler.open();$('#secured_by_plaid').fadeIn()});
$('#plaid_unlink').click(function(e){
e.preventDefault();
$('#manual_container').fadeIn();
$('#plaid_linked').hide();
$('#plaid_link_button').show();
$('#pay_now_button').hide();
$('#add_account_button').show();
$('[name=plaidPublicToken]').remove();
$('[name=plaidAccountId]').remove();
$('[name=account_holder_type],#account_holder_name').attr('required','required');
})
@endif
});
function stripeResponseHandler(status, response) {
var $form = $('.payment-form');
if (response.error) {
// Show the errors on the form
var error = response.error.message;
@if($paymentType == PAYMENT_TYPE_STRIPE_ACH)
if(response.error.param == 'bank_account[country]') {
error = "{{trans('texts.country_not_supported')}}";
}
@endif
$form.find('button').prop('disabled', false);
$('#js-error-message').html(error).fadeIn();
} else {
// response contains id and card, which contains additional card details
var token = response.id;
// Insert the token into the form so it gets submitted to the server
$form.append($('<input type="hidden" name="stripeToken"/>').val(token));
// and submit
$form.get(0).submit();
}
};
function plaidSuccessHandler(public_token, metadata) {
$('#secured_by_plaid').hide()
var $form = $('.payment-form');
$form.append($('<input type="hidden" name="plaidPublicToken"/>').val(public_token));
$form.append($('<input type="hidden" name="plaidAccountId"/>').val(metadata.account_id));
$('#plaid_linked_status').text('{{ trans('texts.plaid_linked_status') }}'.replace(':bank', metadata.institution.name));
$('#manual_container').fadeOut();
$('#plaid_linked').show();
$('#plaid_link_button').hide();
$('[name=account_holder_type],#account_holder_name').removeAttr('required');
var payNowBtn = $('#pay_now_button');
if(payNowBtn.length) {
payNowBtn.show();
$('#add_account_button').hide();
}
};
</script>
@include('payments.tokenization_stripe')
@elseif (isset($accountGateway) && $accountGateway->gateway_id == GATEWAY_WEPAY)
@include('payments.tokenization_wepay')
@else
<script type="text/javascript">
$(function() {
@ -232,7 +22,6 @@
});
</script>
@endif
@stop
@section('content')
@ -283,7 +72,6 @@
{{ Former::populateField('email', $contact->email) }}
@if (!$client->country_id && $client->account->country_id)
{{ Former::populateField('country_id', $client->account->country_id) }}
{{ Former::populateField('country', $client->account->country->iso_3166_2) }}
@endif
@if (!$client->currency_id && $client->account->currency_id)
{{ Former::populateField('currency_id', $client->account->currency_id) }}
@ -351,7 +139,6 @@
<div class="col-md-6">
{!! Former::text('first_name')
->placeholder(trans('texts.first_name'))
->autocomplete('given-name')
->label('') !!}
</div>
<div class="col-md-6">
@ -451,33 +238,32 @@
))->inline()->label(trans('texts.account_holder_type')); !!}
{!! Former::text('account_holder_name')
->label(trans('texts.account_holder_name')) !!}
{!! Former::select('country')
->label(trans('texts.country_id'))
->fromQuery($countries, 'name', 'iso_3166_2')
->addGroupClass('country-select') !!}
{!! Former::select('currency')
->label(trans('texts.currency_id'))
->fromQuery($currencies, 'name', 'code')
->addGroupClass('currency-select') !!}
{!! Former::text('')
->id('routing_number')
->label(trans('texts.routing_number')) !!}
<div class="form-group" style="margin-top:-15px">
<div class="col-md-8 col-md-offset-4">
<div id="bank_name"></div>
</div>
</div>
{!! Former::text('')
->id('account_number')
->label(trans('texts.account_number')) !!}
{!! Former::text('')
->id('confirm_account_number')
->label(trans('texts.confirm_account_number')) !!}
{!! Former::checkbox('authorize_ach')
->text(trans('texts.ach_authorization', ['company'=>$account->getDisplayName()]))
->label(' ') !!}
{!! Former::select('country_id')
->label(trans('texts.country_id'))
->fromQuery($countries, 'name', 'id')
->addGroupClass('country-select') !!}
{!! Former::select('currency')
->label(trans('texts.currency_id'))
->fromQuery($currencies, 'name', 'code')
->addGroupClass('currency-select') !!}
{!! Former::text('')
->id('routing_number')
->label(trans('texts.routing_number')) !!}
<div class="form-group" style="margin-top:-15px">
<div class="col-md-8 col-md-offset-4">
<div id="bank_name"></div>
</div>
</div>
{!! Former::text('')
->id('account_number')
->label(trans('texts.account_number')) !!}
{!! Former::text('')
->id('confirm_account_number')
->label(trans('texts.confirm_account_number')) !!}
</div>
{!! Former::checkbox('authorize_ach')
->text(trans('texts.ach_authorization', ['company'=>$account->getDisplayName()]))
->label(' ') !!}
<div class="col-md-8 col-md-offset-4">
{!! Button::success(strtoupper(trans('texts.add_account')))
->submit()
@ -494,7 +280,7 @@
<h3>{{ trans('texts.paypal') }}</h3>
<div>{{$paypalDetails->firstName}} {{$paypalDetails->lastName}}</div>
<div>{{$paypalDetails->email}}</div>
<input type="hidden" name="payment_method_nonce" value="{{$sourceId}}">
<input type="hidden" name="sourceToken" value="{{$sourceId}}">
<input type="hidden" name="first_name" value="{{$paypalDetails->firstName}}">
<input type="hidden" name="last_name" value="{{$paypalDetails->lastName}}">
<input type="hidden" name="email" value="{{$paypalDetails->email}}">
@ -524,7 +310,7 @@
@if (!empty($braintreeClientToken))
<div id="card_number" class="braintree-hosted form-control"></div>
@else
{!! Former::text($accountGateway->getPublishableStripeKey() ? '' : 'card_number')
{!! Former::text(!empty($tokenize) ? '' : 'card_number')
->id('card_number')
->placeholder(trans('texts.card_number'))
->autocomplete('cc-number')
@ -535,7 +321,7 @@
@if (!empty($braintreeClientToken))
<div id="cvv" class="braintree-hosted form-control"></div>
@else
{!! Former::text($accountGateway->getPublishableStripeKey() ? '' : 'cvv')
{!! Former::text(!empty($tokenize) ? '' : 'cvv')
->id('cvv')
->placeholder(trans('texts.cvv'))
->autocomplete('off')
@ -548,7 +334,7 @@
@if (!empty($braintreeClientToken))
<div id="expiration_month" class="braintree-hosted form-control"></div>
@else
{!! Former::select($accountGateway->getPublishableStripeKey() ? '' : 'expiration_month')
{!! Former::select(!empty($tokenize) ? '' : 'expiration_month')
->id('expiration_month')
->autocomplete('cc-exp-month')
->placeholder(trans('texts.expiration_month'))
@ -571,7 +357,7 @@
@if (!empty($braintreeClientToken))
<div id="expiration_year" class="braintree-hosted form-control"></div>
@else
{!! Former::select($accountGateway->getPublishableStripeKey() ? '' : 'expiration_year')
{!! Former::select(!empty($tokenize) ? '' : 'expiration_year')
->id('expiration_year')
->autocomplete('cc-exp-year')
->placeholder(trans('texts.expiration_year'))
@ -662,7 +448,7 @@
$('#routing_number, #country').on('change keypress keyup keydown paste', function(){setTimeout(function () {
var routingNumber = $('#routing_number').val().replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
if (routingNumber.length != 9 || $("#country").val() != 'US' || routingNumberCache[routingNumber] === false) {
if (routingNumber.length != 9 || $("#country_id").val() != 840 || routingNumberCache[routingNumber] === false) {
$('#bank_name').hide();
} else if (routingNumberCache[routingNumber]) {
$('#bank_name').empty().append(routingNumberCache[routingNumber]).show();

View File

@ -60,7 +60,7 @@
@endif
@if($paymentMethod->payment_type_id == PAYMENT_TYPE_ACH)
@if($paymentMethod->bank_data)
{{ $paymentMethod->bank_data }}
{{ $paymentMethod->bank_data->name }}
@endif
@if($paymentMethod->status == PAYMENT_METHOD_STATUS_NEW)
<a href="#" onclick="completeVerification('{{$paymentMethod->public_id}}','{{$paymentMethod->currency->symbol}}')">({{trans('texts.complete_verification')}})</a>
@ -183,5 +183,6 @@
function setDefault(sourceId) {
$('#default_id').val(sourceId);
$('#defaultSourceForm').submit()
return false;
}
</script>

View File

@ -0,0 +1,67 @@
<script type="text/javascript" src="https://js.braintreegateway.com/js/braintree-2.23.0.min.js"></script>
<script type="text/javascript" >
$(function() {
var $form = $('.payment-form');
braintree.setup("{{ $braintreeClientToken }}", "custom", {
id: "payment-form",
hostedFields: {
number: {
selector: "#card_number",
placeholder: "{{ trans('texts.card_number') }}"
},
cvv: {
selector: "#cvv",
placeholder: "{{ trans('texts.cvv') }}"
},
expirationMonth: {
selector: "#expiration_month",
placeholder: "{{ trans('texts.expiration_month') }}"
},
expirationYear: {
selector: "#expiration_year",
placeholder: "{{ trans('texts.expiration_year') }}"
},
styles: {
'input': {
'font-family': {!! json_encode(Utils::getFromCache($account->getBodyFontId(), 'fonts')['css_stack']) !!},
'font-weight': "{{ Utils::getFromCache($account->getBodyFontId(), 'fonts')['css_weight'] }}",
'font-size': '16px'
}
}
},
onError: function(e) {
$form.find('button').prop('disabled', false);
// Show the errors on the form
if (e.details && e.details.invalidFieldKeys.length) {
var invalidField = e.details.invalidFieldKeys[0];
if (invalidField == 'number') {
$('#js-error-message').html('{{ trans('texts.invalid_card_number') }}').fadeIn();
}
else if (invalidField == 'expirationDate' || invalidField == 'expirationYear' || invalidField == 'expirationMonth') {
$('#js-error-message').html('{{ trans('texts.invalid_expiry') }}').fadeIn();
}
else if (invalidField == 'cvv') {
$('#js-error-message').html('{{ trans('texts.invalid_cvv') }}').fadeIn();
}
}
else {
$('#js-error-message').html(e.message).fadeIn();
}
},
onPaymentMethodReceived: function(e) {
// Insert the token into the form so it gets submitted to the server
$form.append($('<input type="hidden" name="sourceToken"/>').val(e.nonce));
// and submit
$form.get(0).submit();
}
});
$('.payment-form').submit(function(event) {
var $form = $(this);
// Disable the submit button to prevent repeated clicks
$form.find('button').prop('disabled', true);
$('#js-error-message').hide();
});
});
</script>

View File

@ -0,0 +1,154 @@
<script type="text/javascript" src="https://js.stripe.com/v2/"></script>
<script type="text/javascript">
Stripe.setPublishableKey('{{ $accountGateway->getPublishableStripeKey() }}');
$(function() {
var countries = {!! $countries->pluck('iso_3166_2','id') !!};
$('.payment-form').submit(function(event) {
if($('[name=plaidAccountId]').length)return;
var $form = $(this);
var data = {
@if($paymentType == PAYMENT_TYPE_STRIPE_ACH)
account_holder_name: $('#account_holder_name').val(),
account_holder_type: $('[name=account_holder_type]:checked').val(),
currency: $("#currency").val(),
country: countries[$("#country_id").val()],
routing_number: $('#routing_number').val().replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ''),
account_number: $('#account_number').val().replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '')
@else
name: $('#first_name').val() + ' ' + $('#last_name').val(),
address_line1: $('#address1').val(),
address_line2: $('#address2').val(),
address_city: $('#city').val(),
address_state: $('#state').val(),
address_zip: $('#postal_code').val(),
address_country: $("#country_id option:selected").text(),
number: $('#card_number').val(),
cvc: $('#cvv').val(),
exp_month: $('#expiration_month').val(),
exp_year: $('#expiration_year').val()
@endif
};
@if($paymentType == PAYMENT_TYPE_STRIPE_ACH)
// Validate the account details
if (!data.account_holder_type) {
$('#js-error-message').html('{{ trans('texts.missing_account_holder_type') }}').fadeIn();
return false;
}
if (!data.account_holder_name) {
$('#js-error-message').html('{{ trans('texts.missing_account_holder_name') }}').fadeIn();
return false;
}
if (!data.routing_number || !Stripe.bankAccount.validateRoutingNumber(data.routing_number, data.country)) {
$('#js-error-message').html('{{ trans('texts.invalid_routing_number') }}').fadeIn();
return false;
}
if (data.account_number != $('#confirm_account_number').val().replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '')) {
$('#js-error-message').html('{{ trans('texts.account_number_mismatch') }}').fadeIn();
return false;
}
if (!data.account_number || !Stripe.bankAccount.validateAccountNumber(data.account_number, data.country)) {
$('#js-error-message').html('{{ trans('texts.invalid_account_number') }}').fadeIn();
return false;
}
@else
// Validate the card details
if (!Stripe.card.validateCardNumber(data.number)) {
$('#js-error-message').html('{{ trans('texts.invalid_card_number') }}').fadeIn();
return false;
}
if (!Stripe.card.validateExpiry(data.exp_month, data.exp_year)) {
$('#js-error-message').html('{{ trans('texts.invalid_expiry') }}').fadeIn();
return false;
}
if (!Stripe.card.validateCVC(data.cvc)) {
$('#js-error-message').html('{{ trans('texts.invalid_cvv') }}').fadeIn();
return false;
}
@endif
// Disable the submit button to prevent repeated clicks
$form.find('button').prop('disabled', true);
$('#js-error-message').hide();
@if($paymentType == PAYMENT_TYPE_STRIPE_ACH)
Stripe.bankAccount.createToken(data, stripeResponseHandler);
@else
Stripe.card.createToken(data, stripeResponseHandler);
@endif
// Prevent the form from submitting with the default action
return false;
});
@if($accountGateway->getPlaidEnabled())
var plaidHandler = Plaid.create({
selectAccount: true,
env: '{{ $accountGateway->getPlaidEnvironment() }}',
clientName: {!! json_encode($account->getDisplayName()) !!},
key: '{{ $accountGateway->getPlaidPublicKey() }}',
product: 'auth',
onSuccess: plaidSuccessHandler,
onExit : function(){$('#secured_by_plaid').hide()}
});
$('#plaid_link_button').click(function(){plaidHandler.open();$('#secured_by_plaid').fadeIn()});
$('#plaid_unlink').click(function(e){
e.preventDefault();
$('#manual_container').fadeIn();
$('#plaid_linked').hide();
$('#plaid_link_button').show();
$('#pay_now_button').hide();
$('#add_account_button').show();
$('[name=plaidPublicToken]').remove();
$('[name=plaidAccountId]').remove();
$('[name=account_holder_type],#account_holder_name').attr('required','required');
})
@endif
});
function stripeResponseHandler(status, response) {
var $form = $('.payment-form');
if (response.error) {
// Show the errors on the form
var error = response.error.message;
@if($paymentType == PAYMENT_TYPE_STRIPE_ACH)
if(response.error.param == 'bank_account[country]') {
error = "{{trans('texts.country_not_supported')}}";
}
@endif
$form.find('button').prop('disabled', false);
$('#js-error-message').html(error).fadeIn();
} else {
// response contains id and card, which contains additional card details
var token = response.id;
// Insert the token into the form so it gets submitted to the server
$form.append($('<input type="hidden" name="sourceToken"/>').val(token));
// and submit
$form.get(0).submit();
}
};
function plaidSuccessHandler(public_token, metadata) {
$('#secured_by_plaid').hide()
var $form = $('.payment-form');
$form.append($('<input type="hidden" name="plaidPublicToken"/>').val(public_token));
$form.append($('<input type="hidden" name="plaidAccountId"/>').val(metadata.account_id));
$('#plaid_linked_status').text('{{ trans('texts.plaid_linked_status') }}'.replace(':bank', metadata.institution.name));
$('#manual_container').fadeOut();
$('#plaid_linked').show();
$('#plaid_link_button').hide();
$('[name=account_holder_type],#account_holder_name').removeAttr('required');
var payNowBtn = $('#pay_now_button');
if(payNowBtn.length) {
payNowBtn.show();
$('#add_account_button').hide();
}
};
</script>

View File

@ -0,0 +1,62 @@
<script type="text/javascript" src="https://static.wepay.com/min/js/tokenization.v2.js"></script>
<script type="text/javascript">
$(function() {
var countries = {!! $countries->pluck('iso_3166_2','id') !!};
WePay.set_endpoint('{{ WEPAY_ENVIRONMENT }}');
var $form = $('.payment-form');
$('.payment-form').submit(function(event) {
var data = {
client_id: {{ WEPAY_CLIENT_ID }},
user_name: $('#first_name').val() + ' ' + $('#last_name').val(),
email: $('#email').val(),
cc_number: $('#card_number').val(),
cvv: $('#cvv').val(),
expiration_month: $('#expiration_month').val(),
expiration_year: $('#expiration_year').val(),
address: {
address1: $('#address1').val(),
address2: $('#address2').val(),
city: $('#city').val(),
country: countries[$("#country_id").val()]
}
};
if(data.address.country == 'US') {
data.address.zip = $('#postal_code').val();
} else {
data.address.postcode = $('#postal_code').val();
}
// Not including state/province, since WePay wants 2-letter codes and users enter the full name
// Disable the submit button to prevent repeated clicks
$form.find('button').prop('disabled', true);
$('#js-error-message').hide();
var response = WePay.credit_card.create(data, function(response) {
if (response.error) {
// Show the errors on the form
var error = response.error_description;
$form.find('button').prop('disabled', false);
$('#js-error-message').text(error).fadeIn();
} else {
// response contains id and card, which contains additional card details
var token = response.credit_card_id;
// Insert the token into the form so it gets submitted to the server
$form.append($('<input type="hidden" name="sourceToken"/>').val(token));
// and submit
$form.get(0).submit();
}
});
if (response.error) {
// Show the errors on the form
var error = response.error_description;
$form.find('button').prop('disabled', false);
$('#js-error-message').text(error).fadeIn();
}
// Prevent the form from submitting with the default action
return false;
});
});
</script>