mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-10 21:22:58 +01:00
commit
dac9af20ab
@ -22,7 +22,7 @@ class ExpenseCategoryFactory
|
||||
$expense->company_id = $company_id;
|
||||
$expense->name = '';
|
||||
$expense->is_deleted = false;
|
||||
$expense->color = '#fff';
|
||||
$expense->color = '';
|
||||
|
||||
return $expense;
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ class TaskStatusFactory
|
||||
$task_status->user_id = $user_id;
|
||||
$task_status->company_id = $company_id;
|
||||
$task_status->name = '';
|
||||
$task_status->color = '#fff';
|
||||
$task_status->color = '';
|
||||
$task_status->status_order = 9999;
|
||||
|
||||
return $task_status;
|
||||
|
@ -474,6 +474,10 @@ class CompanyController extends BaseController
|
||||
*/
|
||||
public function destroy(DestroyCompanyRequest $request, Company $company)
|
||||
{
|
||||
|
||||
if(Ninja::isHosted() && config('ninja.ninja_default_company_id') == $company->id)
|
||||
return response()->json(['message' => 'Cannot purge this company'], 400);
|
||||
|
||||
$company_count = $company->account->companies->count();
|
||||
$account = $company->account;
|
||||
$account_key = $account->key;
|
||||
|
@ -16,6 +16,7 @@ use App\Utils\CurlUtils;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use stdClass;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class LicenseController extends BaseController
|
||||
{
|
||||
@ -152,7 +153,7 @@ class LicenseController extends BaseController
|
||||
{
|
||||
$account = auth()->user()->company()->account;
|
||||
|
||||
if($account->plan == 'white_label' && $account->plan_expires->lt(now())){
|
||||
if($account->plan == 'white_label' && Carbon::parse($account->plan_expires)->lt(now())){
|
||||
$account->plan = null;
|
||||
$account->plan_expires = null;
|
||||
$account->save();
|
||||
|
@ -82,6 +82,9 @@ class MigrationController extends BaseController
|
||||
*/
|
||||
public function purgeCompany(Company $company)
|
||||
{
|
||||
if(Ninja::isHosted() && config('ninja.ninja_default_company_id') == $company->id)
|
||||
return response()->json(['message' => 'Cannot purge this company'], 400);
|
||||
|
||||
$account = $company->account;
|
||||
$company_id = $company->id;
|
||||
|
||||
@ -102,6 +105,9 @@ class MigrationController extends BaseController
|
||||
|
||||
private function purgeCompanyWithForceFlag(Company $company)
|
||||
{
|
||||
if(Ninja::isHosted() && config('ninja.ninja_default_company_id') == $company->id)
|
||||
return response()->json(['message' => 'Cannot purge this company'], 400);
|
||||
|
||||
$account = $company->account;
|
||||
$company_id = $company->id;
|
||||
|
||||
|
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Requests\Payments\PaymentNotificationWebhookRequest;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Client;
|
||||
use App\Models\CompanyGateway;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Auth;
|
||||
|
||||
class PaymentNotificationWebhookController extends Controller
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
public function __invoke(PaymentNotificationWebhookRequest $request, string $company_key, string $company_gateway_id, string $client_hash)
|
||||
{
|
||||
|
||||
$company_gateway = CompanyGateway::find($this->decodePrimaryKey($company_gateway_id));
|
||||
$client = Client::find($this->decodePrimaryKey($client_hash));
|
||||
|
||||
return $company_gateway
|
||||
->driver($client)
|
||||
->processWebhookRequest($request);
|
||||
}
|
||||
}
|
@ -30,17 +30,22 @@ class UrlSetDb
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
|
||||
if (config('ninja.db.multi_db_enabled')) {
|
||||
$hashids = new Hashids('', 10); //decoded output is _always_ an array.
|
||||
$hashids = new Hashids(config('ninja.hash_salt'), 10);
|
||||
|
||||
//parse URL hash and set DB
|
||||
$segments = explode('-', $request->route('confirmation_code'));
|
||||
|
||||
if(!is_array($segments))
|
||||
return response()->json(['message' => 'Invalid confirmation code'], 403);
|
||||
|
||||
$hashed_db = $hashids->decode($segments[0]);
|
||||
|
||||
MultiDB::setDB(MultiDB::DB_PREFIX.str_pad($hashed_db[0], 2, '0', STR_PAD_LEFT));
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ class StoreExpenseRequest extends Request
|
||||
}
|
||||
|
||||
if(array_key_exists('color', $input) && is_null($input['color']))
|
||||
$input['color'] = '#fff';
|
||||
$input['color'] = '';
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ class StoreExpenseCategoryRequest extends Request
|
||||
$input = $this->decodePrimaryKeys($input);
|
||||
|
||||
if(array_key_exists('color', $input) && is_null($input['color']))
|
||||
$input['color'] = '#fff';
|
||||
$input['color'] = '';
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ class UpdateExpenseCategoryRequest extends Request
|
||||
$input = $this->all();
|
||||
|
||||
if(array_key_exists('color', $input) && is_null($input['color']))
|
||||
$input['color'] = '#fff';
|
||||
$input['color'] = '';
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
|
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\Payments;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Client;
|
||||
use App\Models\Company;
|
||||
use App\Models\CompanyGateway;
|
||||
use App\Models\Payment;
|
||||
use App\Models\PaymentHash;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
|
||||
class PaymentNotificationWebhookRequest extends Request
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
public function authorize()
|
||||
{
|
||||
MultiDB::findAndSetDbByCompanyKey($this->company_key);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
|
||||
}
|
@ -51,7 +51,7 @@ class StoreProjectRequest extends Request
|
||||
|
||||
|
||||
if(array_key_exists('color', $input) && is_null($input['color']))
|
||||
$input['color'] = '#fff';
|
||||
$input['color'] = '';
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ class UpdateProjectRequest extends Request
|
||||
}
|
||||
|
||||
if(array_key_exists('color', $input) && is_null($input['color']))
|
||||
$input['color'] = '#fff';
|
||||
$input['color'] = '';
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ class UpdateTaskRequest extends Request
|
||||
}
|
||||
|
||||
if(array_key_exists('color', $input) && is_null($input['color']))
|
||||
$input['color'] = '#fff';
|
||||
$input['color'] = '';
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ class StoreTaskStatusRequest extends Request
|
||||
$input = $this->all();
|
||||
|
||||
if(array_key_exists('color', $input) && is_null($input['color']))
|
||||
$input['color'] = '#fff';
|
||||
$input['color'] = '';
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ class UpdateTaskStatusRequest extends Request
|
||||
$input = $this->all();
|
||||
|
||||
if(array_key_exists('color', $input) && is_null($input['color']))
|
||||
$input['color'] = '#fff';
|
||||
$input['color'] = '';
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class VersionCheck implements ShouldQueue
|
||||
{
|
||||
@ -49,7 +50,7 @@ class VersionCheck implements ShouldQueue
|
||||
if(!$account)
|
||||
return;
|
||||
|
||||
if($account->plan == 'white_label' && $account->plan_expires && $account->plan_expires->lt(now())){
|
||||
if($account->plan == 'white_label' && $account->plan_expires && Carbon::parse($account->plan_expires)->lt(now())){
|
||||
$account->plan = null;
|
||||
$account->plan_expires = null;
|
||||
$account->save();
|
||||
|
@ -71,7 +71,6 @@ class PaymentNotification implements ShouldQueue
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*Google Analytics Track Revenue*/
|
||||
if (isset($payment->company->google_analytics_key)) {
|
||||
$this->trackRevenue($event);
|
||||
|
@ -55,7 +55,7 @@ class Account extends BaseModel
|
||||
'promo_expires',
|
||||
'discount_expires',
|
||||
'trial_started',
|
||||
'plan_expires'
|
||||
// 'plan_expires'
|
||||
];
|
||||
|
||||
const PLAN_FREE = 'free';
|
||||
@ -120,6 +120,11 @@ class Account extends BaseModel
|
||||
return $this->hasMany(CompanyUser::class);
|
||||
}
|
||||
|
||||
public function owner()
|
||||
{
|
||||
return $this->hasMany(CompanyUser::class)->where('is_owner', true)->first() ? $this->hasMany(CompanyUser::class)->where('is_owner', true)->first()->user : false;
|
||||
}
|
||||
|
||||
public function getPlan()
|
||||
{
|
||||
return $this->plan ?: '';
|
||||
|
@ -371,6 +371,11 @@ class CompanyGateway extends BaseModel
|
||||
return $fee;
|
||||
}
|
||||
|
||||
public function webhookUrl()
|
||||
{
|
||||
return route('payment_webhook', ['company_key' => $this->company->company_key, 'company_gateway_id' => $this->hashed_id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* we need to average out the gateway fees across all the invoices
|
||||
* so lets iterate.
|
||||
@ -412,4 +417,6 @@ class CompanyGateway extends BaseModel
|
||||
return $this
|
||||
->where('id', $this->decodePrimaryKey($value))->firstOrFail();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -21,6 +21,10 @@ class UserPresenter extends EntityPresenter
|
||||
*/
|
||||
public function name()
|
||||
{
|
||||
|
||||
if(!$this->entity)
|
||||
return "No User Object Available";
|
||||
|
||||
$first_name = isset($this->entity->first_name) ? $this->entity->first_name : '';
|
||||
$last_name = isset($this->entity->last_name) ? $this->entity->last_name : '';
|
||||
|
||||
|
@ -67,6 +67,7 @@ class SystemLog extends Model
|
||||
const TYPE_CUSTOM = 306;
|
||||
const TYPE_BRAINTREE = 307;
|
||||
const TYPE_WEPAY = 309;
|
||||
const TYPE_PAYFAST = 310;
|
||||
|
||||
|
||||
const TYPE_QUOTA_EXCEEDED = 400;
|
||||
|
@ -161,7 +161,7 @@ class User extends Authenticatable implements MustVerifyEmail
|
||||
public function setCompany($company)
|
||||
{
|
||||
$this->company = $company;
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -548,6 +548,15 @@ class BaseDriver extends AbstractPaymentDriver
|
||||
);
|
||||
}
|
||||
|
||||
public function genericWebhookUrl()
|
||||
{
|
||||
return route('payment_notification_webhook', [
|
||||
'company_key' => $this->client->company->company_key,
|
||||
'company_gateway_id' => $this->encodePrimaryKey($this->company_gateway->id),
|
||||
'client' => $this->encodePrimaryKey($this->client->id),
|
||||
]);
|
||||
}
|
||||
|
||||
/* Performs an extra iterate on the gatewayTypes() array and passes back only the enabled gateways*/
|
||||
public function gatewayTypeEnabled($type)
|
||||
{
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
namespace App\PaymentDrivers;
|
||||
|
||||
use App\Http\Requests\Payments\PaymentWebhookRequest;
|
||||
use App\Models\ClientGatewayToken;
|
||||
use App\Models\GatewayType;
|
||||
use App\Models\Payment;
|
||||
@ -39,6 +40,22 @@ class DriverTemplate extends BaseDriver
|
||||
|
||||
const SYSTEM_LOG_TYPE = SystemLog::TYPE_STRIPE; //define a constant for your gateway ie TYPE_YOUR_CUSTOM_GATEWAY - set the const in the SystemLog model
|
||||
|
||||
public function init()
|
||||
{
|
||||
return $this; /* This is where you boot the gateway with your auth credentials*/
|
||||
}
|
||||
|
||||
/* Returns an array of gateway types for the payment gateway */
|
||||
public function gatewayTypes(): array
|
||||
{
|
||||
$types = [];
|
||||
|
||||
$types[] = GatewayType::CREDIT_CARD;
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
/* Sets the payment method initialized */
|
||||
public function setPaymentMethod($payment_method_id)
|
||||
{
|
||||
$class = self::$methods[$payment_method_id];
|
||||
@ -75,4 +92,8 @@ class DriverTemplate extends BaseDriver
|
||||
{
|
||||
return $this->payment_method->yourTokenBillingImplmentation(); //this is your custom implementation from here
|
||||
}
|
||||
|
||||
public function processWebhookRequest(PaymentWebhookRequest $request, Payment $payment = null)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
282
app/PaymentDrivers/PayFast/CreditCard.php
Normal file
282
app/PaymentDrivers/PayFast/CreditCard.php
Normal file
@ -0,0 +1,282 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\PaymentDrivers\PayFast;
|
||||
|
||||
use App\Exceptions\PaymentFailed;
|
||||
use App\Jobs\Mail\PaymentFailureMailer;
|
||||
use App\Jobs\Util\SystemLogger;
|
||||
use App\Models\ClientGatewayToken;
|
||||
use App\Models\GatewayType;
|
||||
use App\Models\Payment;
|
||||
use App\Models\PaymentHash;
|
||||
use App\Models\PaymentType;
|
||||
use App\Models\SystemLog;
|
||||
use App\PaymentDrivers\PayFastPaymentDriver;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class CreditCard
|
||||
{
|
||||
|
||||
public $payfast;
|
||||
|
||||
public function __construct(PayFastPaymentDriver $payfast)
|
||||
{
|
||||
$this->payfast = $payfast;
|
||||
}
|
||||
|
||||
/*
|
||||
$data = array();
|
||||
$data['merchant_id'] = $this->getMerchantId();
|
||||
$data['merchant_key'] = $this->getMerchantKey();
|
||||
$data['return_url'] = $this->getReturnUrl();
|
||||
$data['cancel_url'] = $this->getCancelUrl();
|
||||
$data['notify_url'] = $this->getNotifyUrl();
|
||||
|
||||
if ($this->getCard()) {
|
||||
$data['name_first'] = $this->getCard()->getFirstName();
|
||||
$data['name_last'] = $this->getCard()->getLastName();
|
||||
$data['email_address'] = $this->getCard()->getEmail();
|
||||
}
|
||||
|
||||
$data['m_payment_id'] = $this->getTransactionId();
|
||||
$data['amount'] = $this->getAmount();
|
||||
$data['item_name'] = $this->getDescription();
|
||||
$data['custom_int1'] = $this->getCustomInt1();
|
||||
$data['custom_int2'] = $this->getCustomInt2();
|
||||
$data['custom_int3'] = $this->getCustomInt3();
|
||||
$data['custom_int4'] = $this->getCustomInt4();
|
||||
$data['custom_int5'] = $this->getCustomInt5();
|
||||
$data['custom_str1'] = $this->getCustomStr1();
|
||||
$data['custom_str2'] = $this->getCustomStr2();
|
||||
$data['custom_str3'] = $this->getCustomStr3();
|
||||
$data['custom_str4'] = $this->getCustomStr4();
|
||||
$data['custom_str5'] = $this->getCustomStr5();
|
||||
|
||||
if ($this->getPaymentMethod()) {
|
||||
$data['payment_method'] = $this->getPaymentMethod();
|
||||
}
|
||||
|
||||
if (1 == $this->getSubscriptionType()) {
|
||||
$data['subscription_type'] = $this->getSubscriptionType();
|
||||
$data['billing_date'] = $this->getBillingDate();
|
||||
$data['recurring_amount'] = $this->getRecurringAmount();
|
||||
$data['frequency'] = $this->getFrequency();
|
||||
$data['cycles'] = $this->getCycles();
|
||||
}
|
||||
if (2 == $this->getSubscriptionType()) {
|
||||
$data['subscription_type'] = $this->getSubscriptionType();
|
||||
}
|
||||
|
||||
$data['passphrase'] = $this->getParameter('passphrase'); 123456789012aV
|
||||
$data['signature'] = $this->generateSignature($data);
|
||||
*/
|
||||
|
||||
public function authorizeView($data)
|
||||
{
|
||||
$hash = Str::random(32);
|
||||
|
||||
Cache::put($hash, 'cc_auth', 300);
|
||||
|
||||
$data = [
|
||||
'merchant_id' => $this->payfast->company_gateway->getConfigField('merchantId'),
|
||||
'merchant_key' => $this->payfast->company_gateway->getConfigField('merchantKey'),
|
||||
'return_url' => route('client.payment_methods.index'),
|
||||
'cancel_url' => route('client.payment_methods.index'),
|
||||
'notify_url' => $this->payfast->genericWebhookUrl(),
|
||||
'm_payment_id' => $hash,
|
||||
'amount' => 5,
|
||||
'item_name' => 'pre-auth',
|
||||
'item_description' => 'Credit Card Pre Authorization',
|
||||
'subscription_type' => 2,
|
||||
'passphrase' => $this->payfast->company_gateway->getConfigField('passphrase'),
|
||||
];
|
||||
|
||||
$data['signature'] = $this->payfast->generateSignature($data);
|
||||
$data['gateway'] = $this->payfast;
|
||||
$data['payment_endpoint_url'] = $this->payfast->endpointUrl();
|
||||
|
||||
return render('gateways.payfast.authorize', $data);
|
||||
}
|
||||
|
||||
/*
|
||||
'm_payment_id' => NULL,
|
||||
'pf_payment_id' => '1409993',
|
||||
'payment_status' => 'COMPLETE',
|
||||
'item_name' => 'pre-auth',
|
||||
'item_description' => NULL,
|
||||
'amount_gross' => '5.00',
|
||||
'amount_fee' => '-2.53',
|
||||
'amount_net' => '2.47',
|
||||
'custom_str1' => NULL,
|
||||
'custom_str2' => NULL,
|
||||
'custom_str3' => NULL,
|
||||
'custom_str4' => NULL,
|
||||
'custom_str5' => NULL,
|
||||
'custom_int1' => NULL,
|
||||
'custom_int2' => NULL,
|
||||
'custom_int3' => NULL,
|
||||
'custom_int4' => NULL,
|
||||
'custom_int5' => NULL,
|
||||
'name_first' => NULL,
|
||||
'name_last' => NULL,
|
||||
'email_address' => NULL,
|
||||
'merchant_id' => '10023100',
|
||||
'token' => '34b66bc2-3c54-9590-03ea-42ee8b89922a',
|
||||
'billing_date' => '2021-07-05',
|
||||
'signature' => 'ebdb4ca937d0e3f43462841c0afc6ad9',
|
||||
'q' => '/payment_notification_webhook/EhbnVYyzJZyccY85hcHIkIzNPI2rtHzznAyyyG73oSxZidAdN9gf8BvAKDomqeHp/4openRe7Az/WPe99p3eLy',
|
||||
*/
|
||||
public function authorizeResponse($request)
|
||||
{
|
||||
$data = $request->all();
|
||||
|
||||
$cgt = [];
|
||||
$cgt['token'] = $data['token'];
|
||||
$cgt['payment_method_id'] = GatewayType::CREDIT_CARD;
|
||||
|
||||
$payment_meta = new \stdClass;
|
||||
$payment_meta->exp_month = 'xx';
|
||||
$payment_meta->exp_year = 'xx';
|
||||
$payment_meta->brand = 'CC';
|
||||
$payment_meta->last4 = 'xxxx';
|
||||
$payment_meta->type = GatewayType::CREDIT_CARD;
|
||||
|
||||
$cgt['payment_meta'] = $payment_meta;
|
||||
|
||||
$token = $this->payfast->storeGatewayToken($cgt, []);
|
||||
|
||||
return response()->json([], 200);
|
||||
|
||||
}
|
||||
|
||||
public function paymentView($data)
|
||||
{
|
||||
|
||||
$payfast_data = [
|
||||
'merchant_id' => $this->payfast->company_gateway->getConfigField('merchantId'),
|
||||
'merchant_key' => $this->payfast->company_gateway->getConfigField('merchantKey'),
|
||||
'return_url' => route('client.payments.index'),
|
||||
'cancel_url' => route('client.payment_methods.index'),
|
||||
'notify_url' => $this->payfast->genericWebhookUrl(),
|
||||
'm_payment_id' => $data['payment_hash'],
|
||||
'amount' => $data['amount_with_fee'],
|
||||
'item_name' => 'purchase',
|
||||
'item_description' => ctrans('texts.invoices') . ': ' . collect($data['invoices'])->pluck('invoice_number'),
|
||||
'passphrase' => $this->payfast->company_gateway->getConfigField('passphrase'),
|
||||
];
|
||||
|
||||
$payfast_data['signature'] = $this->payfast->generateSignature($payfast_data);
|
||||
$payfast_data['gateway'] = $this->payfast;
|
||||
$payfast_data['payment_endpoint_url'] = $this->payfast->endpointUrl();
|
||||
|
||||
return render('gateways.payfast.pay', array_merge($data, $payfast_data));
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
[2021-07-05 11:21:24] local.INFO: array (
|
||||
'm_payment_id' => 'B7G9Q2vPhqkLEoMwwY1paXvPGuFxpbDe',
|
||||
'pf_payment_id' => '1410364',
|
||||
'payment_status' => 'COMPLETE',
|
||||
'item_name' => 'purchase',
|
||||
'item_description' => 'Invoices: ["0001"]',
|
||||
'amount_gross' => '100.00',
|
||||
'amount_fee' => '-2.30',
|
||||
'amount_net' => '97.70',
|
||||
'custom_str1' => NULL,
|
||||
'custom_str2' => NULL,
|
||||
'custom_str3' => NULL,
|
||||
'custom_str4' => NULL,
|
||||
'custom_str5' => NULL,
|
||||
'custom_int1' => NULL,
|
||||
'custom_int2' => NULL,
|
||||
'custom_int3' => NULL,
|
||||
'custom_int4' => NULL,
|
||||
'custom_int5' => NULL,
|
||||
'name_first' => NULL,
|
||||
'name_last' => NULL,
|
||||
'email_address' => NULL,
|
||||
'merchant_id' => '10023100',
|
||||
'signature' => '3ed27638479fd65cdffb0f4910679d10',
|
||||
'q' => '/payment_notification_webhook/EhbnVYyzJZyccY85hcHIkIzNPI2rtHzznAyyyG73oSxZidAdN9gf8BvAKDomqeHp/4openRe7Az/WPe99p3eLy',
|
||||
)
|
||||
|
||||
*/
|
||||
public function paymentResponse(Request $request)
|
||||
{
|
||||
$response_array = $request->all();
|
||||
|
||||
$state = [
|
||||
'server_response' => $request->all(),
|
||||
'payment_hash' => $request->input('m_payment_id'),
|
||||
];
|
||||
|
||||
$this->payfast->payment_hash->data = array_merge((array) $this->payfast->payment_hash->data, $state);
|
||||
$this->payfast->payment_hash->save();
|
||||
|
||||
if($response_array['payment_status'] == 'COMPLETE') {
|
||||
|
||||
$this->payfast->logSuccessfulGatewayResponse(['response' => $response_array, 'data' => $this->payfast->payment_hash], SystemLog::TYPE_PAYFAST);
|
||||
|
||||
return $this->processSuccessfulPayment($response_array);
|
||||
}
|
||||
else {
|
||||
$this->processUnsuccessfulPayment($response_array);
|
||||
}
|
||||
}
|
||||
|
||||
private function processSuccessfulPayment($response_array)
|
||||
{
|
||||
|
||||
$payment_record = [];
|
||||
$payment_record['amount'] = $response_array['amount_gross'];
|
||||
$payment_record['payment_type'] = PaymentType::CREDIT_CARD_OTHER;
|
||||
$payment_record['gateway_type_id'] = GatewayType::CREDIT_CARD;
|
||||
$payment_record['transaction_reference'] = $response_array['pf_payment_id'];
|
||||
|
||||
$payment = $this->payfast->createPayment($payment_record, Payment::STATUS_COMPLETED);
|
||||
|
||||
return redirect()->route('client.payments.show', ['payment' => $this->payfast->encodePrimaryKey($payment->id)]);
|
||||
}
|
||||
|
||||
private function processUnsuccessfulPayment($server_response)
|
||||
{
|
||||
PaymentFailureMailer::dispatch($this->payfast->client, $server_response->cancellation_reason, $this->payfast->client->company, $server_response->amount);
|
||||
|
||||
PaymentFailureMailer::dispatch(
|
||||
$this->payfast->client,
|
||||
$server_response,
|
||||
$this->payfast->client->company,
|
||||
$server_response['amount_gross']
|
||||
);
|
||||
|
||||
$message = [
|
||||
'server_response' => $server_response,
|
||||
'data' => $this->payfast->payment_hash->data,
|
||||
];
|
||||
|
||||
SystemLogger::dispatch(
|
||||
$message,
|
||||
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||
SystemLog::EVENT_GATEWAY_FAILURE,
|
||||
SystemLog::TYPE_PAYFAST,
|
||||
$this->payfast->client,
|
||||
$this->payfast->client->company,
|
||||
);
|
||||
|
||||
throw new PaymentFailed('Failed to process the payment.', 500);
|
||||
}
|
||||
|
||||
}
|
184
app/PaymentDrivers/PayFast/Token.php
Normal file
184
app/PaymentDrivers/PayFast/Token.php
Normal file
@ -0,0 +1,184 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\PaymentDrivers\PayFast;
|
||||
|
||||
use App\Exceptions\PaymentFailed;
|
||||
use App\Jobs\Mail\PaymentFailureMailer;
|
||||
use App\Jobs\Util\SystemLogger;
|
||||
use App\Models\ClientGatewayToken;
|
||||
use App\Models\GatewayType;
|
||||
use App\Models\Payment;
|
||||
use App\Models\PaymentHash;
|
||||
use App\Models\PaymentType;
|
||||
use App\Models\SystemLog;
|
||||
use App\PaymentDrivers\PayFastPaymentDriver;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Str;
|
||||
use GuzzleHttp\RequestOptions;
|
||||
|
||||
class Token
|
||||
{
|
||||
|
||||
public $payfast;
|
||||
|
||||
//https://api.payfast.co.za/subscriptions/dc0521d3-55fe-269b-fa00-b647310d760f/adhoc
|
||||
|
||||
public function __construct(PayFastPaymentDriver $payfast)
|
||||
{
|
||||
$this->payfast = $payfast;
|
||||
}
|
||||
|
||||
// Attributes
|
||||
// merchant-id
|
||||
// integer, 8 char | REQUIRED
|
||||
// Header, the Merchant ID as given by the PayFast system.
|
||||
// version
|
||||
// string | REQUIRED
|
||||
// Header, the PayFast API version (i.e. v1).
|
||||
// timestamp
|
||||
// ISO-8601 date and time | REQUIRED
|
||||
// Header, the current timestamp (YYYY-MM-DDTHH:MM:SS[+HH:MM]).
|
||||
// signature
|
||||
// string | REQUIRED
|
||||
// Header, MD5 hash of the alphabetised submitted header and body variables, as well as the passphrase. Characters must be in lower case.
|
||||
// amount
|
||||
// integer | REQUIRED
|
||||
// Body, the amount which the buyer must pay, in cents (ZAR), no decimals.
|
||||
// item_name
|
||||
// string, 100 char | REQUIRED
|
||||
// Body, the name of the item being charged for.
|
||||
// item_description
|
||||
// string, 255 char | OPTIONAL
|
||||
// Body, the description of the item being charged for.
|
||||
// itn
|
||||
// boolean | OPTIONAL
|
||||
// Body, specify whether an ITN must be sent for the tokenization payment (true by default).
|
||||
// m_payment_id
|
||||
// string, 100 char | OPTIONAL
|
||||
// Body, unique payment ID on the merchant’s system.
|
||||
// cc_cvv
|
||||
// numeric | OPTIONAL
|
||||
|
||||
|
||||
public function tokenBilling(ClientGatewayToken $cgt, PaymentHash $payment_hash)
|
||||
{
|
||||
|
||||
$amount = array_sum(array_column($payment_hash->invoices(), 'amount')) + $payment_hash->fee_total;
|
||||
$amount = round(($amount * pow(10, $this->payfast->client->currency()->precision)),0);
|
||||
|
||||
$header =[
|
||||
'merchant-id' => $this->payfast->company_gateway->getConfigField('merchantId'),
|
||||
'timestamp' => now()->format('c'),
|
||||
'version' => 'v1',
|
||||
];
|
||||
|
||||
nlog($header);
|
||||
|
||||
$body = [
|
||||
'amount' => $amount,
|
||||
'item_name' => 'purchase',
|
||||
'item_description' => ctrans('texts.invoices') . ': ' . collect($payment_hash->invoices())->pluck('invoice_number'),
|
||||
'm_payment_id' => $payment_hash->hash,
|
||||
'passphrase' => $this->payfast->company_gateway->getConfigField('passphrase'),
|
||||
];
|
||||
|
||||
$header['signature'] = $this->genSig(array_merge($header, $body));
|
||||
|
||||
nlog($header['signature']);
|
||||
nlog($header['timestamp']);
|
||||
nlog($this->payfast->company_gateway->getConfigField('merchantId'));
|
||||
|
||||
$result = $this->send($header, $body, $cgt->token);
|
||||
|
||||
nlog($result);
|
||||
|
||||
// /*Refactor and push to BaseDriver*/
|
||||
// if ($data['response'] != null && $data['response']->getMessages()->getResultCode() == 'Ok') {
|
||||
|
||||
// $response = $data['response'];
|
||||
|
||||
// $this->storePayment($payment_hash, $data);
|
||||
|
||||
// $vars = [
|
||||
// 'invoices' => $payment_hash->invoices(),
|
||||
// 'amount' => $amount,
|
||||
// ];
|
||||
|
||||
// $logger_message = [
|
||||
// 'server_response' => $response->getTransactionResponse()->getTransId(),
|
||||
// 'data' => $this->formatGatewayResponse($data, $vars),
|
||||
// ];
|
||||
|
||||
// SystemLogger::dispatch($logger_message, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_SUCCESS, SystemLog::TYPE_AUTHORIZE, $this->authorize->client, $this->authorize->client->company);
|
||||
|
||||
// return true;
|
||||
// } else {
|
||||
|
||||
// $vars = [
|
||||
// 'invoices' => $payment_hash->invoices(),
|
||||
// 'amount' => $amount,
|
||||
// ];
|
||||
|
||||
// $logger_message = [
|
||||
// 'server_response' => $response->getTransactionResponse()->getTransId(),
|
||||
// 'data' => $this->formatGatewayResponse($data, $vars),
|
||||
// ];
|
||||
|
||||
// PaymentFailureMailer::dispatch($this->authorize->client, $response->getTransactionResponse()->getTransId(), $this->authorize->client->company, $amount);
|
||||
|
||||
// SystemLogger::dispatch($logger_message, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_AUTHORIZE, $this->authorize->client, $this->authorize->client->company);
|
||||
|
||||
// return false;
|
||||
// }
|
||||
}
|
||||
|
||||
private function genSig($data)
|
||||
{
|
||||
$fields = [];
|
||||
|
||||
ksort($data);
|
||||
|
||||
foreach($data as $key => $value)
|
||||
{
|
||||
if (!empty($data[$key])) {
|
||||
$fields[$key] = $data[$key];
|
||||
}
|
||||
}
|
||||
|
||||
return md5(http_build_query($fields));
|
||||
}
|
||||
|
||||
private function send($headers, $body, $token)
|
||||
{
|
||||
$client = new \GuzzleHttp\Client(
|
||||
[
|
||||
'headers' => $headers,
|
||||
]);
|
||||
|
||||
try {
|
||||
$response = $client->post("https://api.payfast.co.za/subscriptions/{$token}/adhoc?testing=true",[
|
||||
RequestOptions::JSON => ['body' => $body], RequestOptions::ALLOW_REDIRECTS => false
|
||||
]);
|
||||
|
||||
return json_decode($response->getBody(),true);
|
||||
}
|
||||
catch(\Exception $e)
|
||||
{
|
||||
|
||||
nlog($e->getMessage());
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
202
app/PaymentDrivers/PayFastPaymentDriver.php
Normal file
202
app/PaymentDrivers/PayFastPaymentDriver.php
Normal file
@ -0,0 +1,202 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\PaymentDrivers;
|
||||
|
||||
use App\Models\ClientGatewayToken;
|
||||
use App\Models\GatewayType;
|
||||
use App\Models\Payment;
|
||||
use App\Models\PaymentHash;
|
||||
use App\Models\SystemLog;
|
||||
use App\PaymentDrivers\PayFast\CreditCard;
|
||||
use App\PaymentDrivers\PayFast\Token;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
class PayFastPaymentDriver extends BaseDriver
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
public $refundable = false; //does this gateway support refunds?
|
||||
|
||||
public $token_billing = false; //does this gateway support token billing?
|
||||
|
||||
public $can_authorise_credit_card = true; //does this gateway support authorizations?
|
||||
|
||||
public $payfast; //initialized gateway
|
||||
|
||||
public $payment_method; //initialized payment method
|
||||
|
||||
public static $methods = [
|
||||
GatewayType::CREDIT_CARD => CreditCard::class,
|
||||
];
|
||||
|
||||
const SYSTEM_LOG_TYPE = SystemLog::TYPE_PAYFAST;
|
||||
|
||||
//developer resources
|
||||
//https://sandbox.payfast.co.za/
|
||||
|
||||
public function gatewayTypes(): array
|
||||
{
|
||||
$types = [];
|
||||
|
||||
if($this->client->currency()->code == 'ZAR')
|
||||
$types[] = GatewayType::CREDIT_CARD;
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
public function endpointUrl()
|
||||
{
|
||||
if($this->company_gateway->getConfigField('testMode'))
|
||||
return 'https://sandbox.payfast.co.za/eng/process';
|
||||
|
||||
return 'https://www.payfast.co.za/eng/process';
|
||||
}
|
||||
|
||||
public function init()
|
||||
{
|
||||
|
||||
try{
|
||||
|
||||
$this->payfast = new \PayFast\PayFastPayment(
|
||||
[
|
||||
'merchantId' => $this->company_gateway->getConfigField('merchantId'),
|
||||
'merchantKey' => $this->company_gateway->getConfigField('merchantKey'),
|
||||
'passPhrase' => $this->company_gateway->getConfigField('passPhrase'),
|
||||
'testMode' => $this->company_gateway->getConfigField('testMode')
|
||||
]
|
||||
);
|
||||
|
||||
} catch(Exception $e) {
|
||||
|
||||
echo '##PAYFAST## There was an exception: '.$e->getMessage();
|
||||
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setPaymentMethod($payment_method_id)
|
||||
{
|
||||
$class = self::$methods[$payment_method_id];
|
||||
$this->payment_method = new $class($this);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function authorizeView(array $data)
|
||||
{
|
||||
return $this->payment_method->authorizeView($data);
|
||||
}
|
||||
|
||||
public function authorizeResponse($request)
|
||||
{
|
||||
return $this->payment_method->authorizeResponse($request);
|
||||
}
|
||||
|
||||
public function processPaymentView(array $data)
|
||||
{
|
||||
return $this->payment_method->paymentView($data); //this is your custom implementation from here
|
||||
}
|
||||
|
||||
public function processPaymentResponse($request)
|
||||
{
|
||||
return $this->payment_method->paymentResponse($request); //this is your custom implementation from here
|
||||
}
|
||||
|
||||
public function refund(Payment $payment, $amount, $return_client_response = false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function tokenBilling(ClientGatewayToken $cgt, PaymentHash $payment_hash)
|
||||
{
|
||||
return (new Token($this))->tokenBilling($cgt, $payment_hash);
|
||||
}
|
||||
|
||||
public function generateSignature($data)
|
||||
{
|
||||
$fields = array();
|
||||
|
||||
// specific order required by PayFast
|
||||
// @see https://developers.payfast.co.za/documentation/#checkout-page
|
||||
foreach (array('merchant_id', 'merchant_key', 'return_url', 'cancel_url', 'notify_url', 'name_first',
|
||||
'name_last', 'email_address', 'cell_number',
|
||||
/**
|
||||
* Transaction Details
|
||||
*/
|
||||
'm_payment_id', 'amount', 'item_name', 'item_description',
|
||||
/**
|
||||
* Custom return data
|
||||
*/
|
||||
'custom_int1', 'custom_int2', 'custom_int3', 'custom_int4', 'custom_int5',
|
||||
'custom_str1', 'custom_str2', 'custom_str3', 'custom_str4', 'custom_str5',
|
||||
/**
|
||||
* Email confirmation
|
||||
*/
|
||||
'email_confirmation', 'confirmation_address',
|
||||
/**
|
||||
* Payment Method
|
||||
*/
|
||||
'payment_method',
|
||||
/**
|
||||
* Subscriptions
|
||||
*/
|
||||
'subscription_type', 'billing_date', 'recurring_amount', 'frequency', 'cycles',
|
||||
/**
|
||||
* Passphrase for md5 signature generation
|
||||
*/
|
||||
'passphrase') as $key) {
|
||||
if (!empty($data[$key])) {
|
||||
$fields[$key] = $data[$key];
|
||||
}
|
||||
}
|
||||
|
||||
return md5(http_build_query($fields));
|
||||
}
|
||||
|
||||
|
||||
public function processWebhookRequest(Request $request, Payment $payment = null)
|
||||
{
|
||||
|
||||
$data = $request->all();
|
||||
nlog($data);
|
||||
|
||||
if(array_key_exists('m_payment_id', $data))
|
||||
{
|
||||
|
||||
$hash = Cache::get($data['m_payment_id']);
|
||||
|
||||
switch ($hash)
|
||||
{
|
||||
case 'cc_auth':
|
||||
return $this->setPaymentMethod(GatewayType::CREDIT_CARD)
|
||||
->authorizeResponse($request);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
$payment_hash = PaymentHash::whereRaw('BINARY `hash`= ?', [$data['m_payment_id']])->first();
|
||||
|
||||
return $this->setPaymentMethod(GatewayType::CREDIT_CARD)
|
||||
->setPaymentHash($payment_hash)
|
||||
->processPaymentResponse($request);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
return response()->json([], 200);
|
||||
|
||||
}
|
||||
}
|
@ -117,7 +117,6 @@ use WePayCommon;
|
||||
nlog("authorize the card first!");
|
||||
|
||||
$response = $this->wepay_payment_driver->wepay->request('credit_card/authorize', array(
|
||||
// 'callback_uri' => route('payment_webhook', ['company_key' => $this->wepay_payment_driver->company_gateway->company->company_key, 'company_gateway_id' => $this->wepay_payment_driver->company_gateway->hashed_id]),
|
||||
'client_id' => config('ninja.wepay.client_id'),
|
||||
'client_secret' => config('ninja.wepay.client_secret'),
|
||||
'credit_card_id' => (int)$request->input('credit_card_id'),
|
||||
|
@ -71,7 +71,8 @@ class HandleReversal extends AbstractService
|
||||
$credit = CreditFactory::create($this->invoice->company_id, $this->invoice->user_id);
|
||||
$credit->client_id = $this->invoice->client_id;
|
||||
$credit->invoice_id = $this->invoice->id;
|
||||
|
||||
$credit->date = now();
|
||||
|
||||
$item = InvoiceItemFactory::create();
|
||||
$item->quantity = 1;
|
||||
$item->cost = (float) $total_paid;
|
||||
|
@ -77,7 +77,8 @@ class SubscriptionService
|
||||
$recurring_invoice->next_send_date = now();
|
||||
$recurring_invoice = $recurring_invoice_repo->save([], $recurring_invoice);
|
||||
$recurring_invoice->next_send_date = $recurring_invoice->nextSendDate();
|
||||
|
||||
$recurring_invoice->auto_bill = $this->subscription->auto_bill;
|
||||
|
||||
/* Start the recurring service */
|
||||
$recurring_invoice->service()
|
||||
->start()
|
||||
|
@ -26,9 +26,9 @@ use App\Utils\CurlUtils;
|
||||
use App\Utils\HtmlEngine;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Response;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class Phantom
|
||||
|
@ -64,6 +64,7 @@
|
||||
"maennchen/zipstream-php": "^1.2",
|
||||
"nwidart/laravel-modules": "^8.0",
|
||||
"omnipay/paypal": "^3.0",
|
||||
"payfast/payfast-php-sdk": "^1.1",
|
||||
"pragmarx/google2fa": "^8.0",
|
||||
"predis/predis": "^1.1",
|
||||
"sentry/sentry-laravel": "^2",
|
||||
|
101
composer.lock
generated
101
composer.lock
generated
@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "16a38ffa3774d9d28a9f4c49366baac0",
|
||||
"content-hash": "25a0cbf18fc238305c7ea640c49ba89a",
|
||||
"packages": [
|
||||
{
|
||||
"name": "asm/php-ansible",
|
||||
@ -159,16 +159,16 @@
|
||||
},
|
||||
{
|
||||
"name": "aws/aws-sdk-php",
|
||||
"version": "3.185.7",
|
||||
"version": "3.185.10",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/aws/aws-sdk-php.git",
|
||||
"reference": "7c0cd260e749374b5df247c4768c8f33f9a604e4"
|
||||
"reference": "667a83e4a18cb75db3ce74162efc97123da96261"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/7c0cd260e749374b5df247c4768c8f33f9a604e4",
|
||||
"reference": "7c0cd260e749374b5df247c4768c8f33f9a604e4",
|
||||
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/667a83e4a18cb75db3ce74162efc97123da96261",
|
||||
"reference": "667a83e4a18cb75db3ce74162efc97123da96261",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -243,9 +243,9 @@
|
||||
"support": {
|
||||
"forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
|
||||
"issues": "https://github.com/aws/aws-sdk-php/issues",
|
||||
"source": "https://github.com/aws/aws-sdk-php/tree/3.185.7"
|
||||
"source": "https://github.com/aws/aws-sdk-php/tree/3.185.10"
|
||||
},
|
||||
"time": "2021-07-06T18:16:14+00:00"
|
||||
"time": "2021-07-09T19:21:22+00:00"
|
||||
},
|
||||
{
|
||||
"name": "bacon/bacon-qr-code",
|
||||
@ -4113,16 +4113,16 @@
|
||||
},
|
||||
{
|
||||
"name": "league/oauth1-client",
|
||||
"version": "v1.9.0",
|
||||
"version": "v1.9.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/oauth1-client.git",
|
||||
"reference": "1e7e6be2dc543bf466236fb171e5b20e1b06aee6"
|
||||
"reference": "19a3ce488bb1547c906209e8293199ec34eaa5b1"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/oauth1-client/zipball/1e7e6be2dc543bf466236fb171e5b20e1b06aee6",
|
||||
"reference": "1e7e6be2dc543bf466236fb171e5b20e1b06aee6",
|
||||
"url": "https://api.github.com/repos/thephpleague/oauth1-client/zipball/19a3ce488bb1547c906209e8293199ec34eaa5b1",
|
||||
"reference": "19a3ce488bb1547c906209e8293199ec34eaa5b1",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -4182,9 +4182,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/thephpleague/oauth1-client/issues",
|
||||
"source": "https://github.com/thephpleague/oauth1-client/tree/v1.9.0"
|
||||
"source": "https://github.com/thephpleague/oauth1-client/tree/v1.9.1"
|
||||
},
|
||||
"time": "2021-01-20T01:40:53+00:00"
|
||||
"time": "2021-07-07T22:54:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/omnipay",
|
||||
@ -4251,16 +4251,16 @@
|
||||
},
|
||||
{
|
||||
"name": "livewire/livewire",
|
||||
"version": "v2.5.1",
|
||||
"version": "v2.5.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/livewire/livewire.git",
|
||||
"reference": "a4ffb135693e7982e5b982ca203f5dc7a7ae1126"
|
||||
"reference": "1ca6757c78dbead4db7f52a72dabb8b27efcb3f6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/livewire/livewire/zipball/a4ffb135693e7982e5b982ca203f5dc7a7ae1126",
|
||||
"reference": "a4ffb135693e7982e5b982ca203f5dc7a7ae1126",
|
||||
"url": "https://api.github.com/repos/livewire/livewire/zipball/1ca6757c78dbead4db7f52a72dabb8b27efcb3f6",
|
||||
"reference": "1ca6757c78dbead4db7f52a72dabb8b27efcb3f6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -4311,7 +4311,7 @@
|
||||
"description": "A front-end framework for Laravel.",
|
||||
"support": {
|
||||
"issues": "https://github.com/livewire/livewire/issues",
|
||||
"source": "https://github.com/livewire/livewire/tree/v2.5.1"
|
||||
"source": "https://github.com/livewire/livewire/tree/v2.5.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -4319,7 +4319,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2021-06-15T13:24:48+00:00"
|
||||
"time": "2021-07-08T13:58:45+00:00"
|
||||
},
|
||||
{
|
||||
"name": "maennchen/zipstream-php",
|
||||
@ -5334,6 +5334,57 @@
|
||||
},
|
||||
"time": "2020-10-15T08:29:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "payfast/payfast-php-sdk",
|
||||
"version": "v1.1.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/PayFast/payfast-php-sdk.git",
|
||||
"reference": "1372980e38f381b84eed7eb46a40d5819a4fe58c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/PayFast/payfast-php-sdk/zipball/1372980e38f381b84eed7eb46a40d5819a4fe58c",
|
||||
"reference": "1372980e38f381b84eed7eb46a40d5819a4fe58c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"guzzlehttp/guzzle": ">=6.0.0",
|
||||
"php": ">=7.2.5"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"PayFast\\": "lib/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Claire Grant",
|
||||
"email": "claire.grant@payfast.co.za"
|
||||
}
|
||||
],
|
||||
"description": "PayFast PHP Library",
|
||||
"keywords": [
|
||||
"api",
|
||||
"onsite",
|
||||
"payfast",
|
||||
"php"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/PayFast/payfast-php-sdk/issues",
|
||||
"source": "https://github.com/PayFast/payfast-php-sdk/tree/v1.1.2"
|
||||
},
|
||||
"time": "2021-03-15T19:58:26+00:00"
|
||||
},
|
||||
{
|
||||
"name": "php-http/client-common",
|
||||
"version": "2.4.0",
|
||||
@ -7319,16 +7370,16 @@
|
||||
},
|
||||
{
|
||||
"name": "stripe/stripe-php",
|
||||
"version": "v7.87.0",
|
||||
"version": "v7.88.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/stripe/stripe-php.git",
|
||||
"reference": "9392f03cb8d8803bf8273378ce42d5cbbf1e24fc"
|
||||
"reference": "7203d00ba9b09830c0c5d5c06a9558db43b8e0ea"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/stripe/stripe-php/zipball/9392f03cb8d8803bf8273378ce42d5cbbf1e24fc",
|
||||
"reference": "9392f03cb8d8803bf8273378ce42d5cbbf1e24fc",
|
||||
"url": "https://api.github.com/repos/stripe/stripe-php/zipball/7203d00ba9b09830c0c5d5c06a9558db43b8e0ea",
|
||||
"reference": "7203d00ba9b09830c0c5d5c06a9558db43b8e0ea",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -7374,9 +7425,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/stripe/stripe-php/issues",
|
||||
"source": "https://github.com/stripe/stripe-php/tree/v7.87.0"
|
||||
"source": "https://github.com/stripe/stripe-php/tree/v7.88.0"
|
||||
},
|
||||
"time": "2021-06-30T18:22:47+00:00"
|
||||
"time": "2021-07-09T20:01:03+00:00"
|
||||
},
|
||||
{
|
||||
"name": "swiftmailer/swiftmailer",
|
||||
|
@ -109,7 +109,6 @@ return [
|
||||
'gelf' => [
|
||||
'driver' => 'custom',
|
||||
|
||||
|
||||
'via' => \Hedii\LaravelGelfLogger\GelfLoggerFactory::class,
|
||||
|
||||
// This optional option determines the processors that should be
|
||||
|
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
use App\Models\Gateway;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class ActivatePayfastPaymentDriver extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Gateway::whereIn('id', [11])->update(['visible' => 1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
@extends('portal.ninja2020.layout.payments', ['gateway_title' => ctrans('texts.credit_card'), 'card_title' => ctrans('texts.credit_card')])
|
||||
|
||||
@section('gateway_head')
|
||||
<meta name="contact-email" content="{{ $contact->email }}">
|
||||
<meta name="client-postal-code" content="{{ $contact->client->postal_code }}">
|
||||
@endsection
|
||||
|
||||
@section('gateway_content')
|
||||
<form action="{{ $payment_endpoint_url }}" method="post" id="server_response">
|
||||
<input type="hidden" name="merchant_id" value="{{ $merchant_id }}">
|
||||
<input type="hidden" name="merchant_key" value="{{ $merchant_key }}">
|
||||
<input type="hidden" name="return_url" value="{{ $return_url }}">
|
||||
<input type="hidden" name="cancel_url" value="{{ $cancel_url }}">
|
||||
<input type="hidden" name="notify_url" value="{{ $notify_url }}">
|
||||
<input type="hidden" name="m_payment_id" value="{{ $m_payment_id }}">
|
||||
<input type="hidden" name="amount" value="{{ $amount }}">
|
||||
<input type="hidden" name="item_name" value="{{ $item_name }}">
|
||||
<input type="hidden" name="item_description" value="{{ $item_description}}">
|
||||
<input type="hidden" name="subscription_type" value="{{ $subscription_type }}">
|
||||
<input type="hidden" name="passphrase" value="{{ $passphrase }}">
|
||||
<input type="hidden" name="signature" value="{{ $signature }}">
|
||||
|
||||
|
||||
@if(!Request::isSecure())
|
||||
<p class="alert alert-failure">{{ ctrans('texts.https_required') }}</p>
|
||||
@endif
|
||||
|
||||
<div class="alert alert-failure mb-4" hidden id="errors"></div>
|
||||
|
||||
@component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.method')])
|
||||
{{ ctrans('texts.credit_card') }}
|
||||
@endcomponent
|
||||
|
||||
<div class="bg-white px-4 py-5 flex justify-end">
|
||||
<button
|
||||
type="submit"
|
||||
id="{{ $id ?? 'pay-now' }}"
|
||||
class="button button-primary bg-primary {{ $class ?? '' }}">
|
||||
<span>{{ ctrans('texts.add_payment_method') }}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
@endsection
|
||||
|
||||
@section('gateway_footer')
|
||||
|
||||
@endsection
|
@ -0,0 +1,74 @@
|
||||
@extends('portal.ninja2020.layout.payments', ['gateway_title' => ctrans('texts.credit_card'), 'card_title' => ctrans('texts.credit_card')])
|
||||
|
||||
@section('gateway_head')
|
||||
<meta name="contact-email" content="{{ $contact->email }}">
|
||||
<meta name="client-postal-code" content="{{ $contact->client->postal_code }}">
|
||||
@endsection
|
||||
|
||||
@section('gateway_content')
|
||||
<form action="{{ $payment_endpoint_url }}" method="post" id="server_response">
|
||||
<input type="hidden" name="merchant_id" value="{{ $merchant_id }}">
|
||||
<input type="hidden" name="merchant_key" value="{{ $merchant_key }}">
|
||||
<input type="hidden" name="return_url" value="{{ $return_url }}">
|
||||
<input type="hidden" name="cancel_url" value="{{ $cancel_url }}">
|
||||
<input type="hidden" name="notify_url" value="{{ $notify_url }}">
|
||||
<input type="hidden" name="m_payment_id" value="{{ $m_payment_id }}">
|
||||
<input type="hidden" name="amount" value="{{ $amount }}">
|
||||
<input type="hidden" name="item_name" value="{{ $item_name }}">
|
||||
<input type="hidden" name="item_description" value="{{ $item_description}}">
|
||||
<input type="hidden" name="passphrase" value="{{ $passphrase }}">
|
||||
<input type="hidden" name="signature" value="{{ $signature }}">
|
||||
|
||||
<div class="alert alert-failure mb-4" hidden id="errors"></div>
|
||||
|
||||
@component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.method')])
|
||||
{{ ctrans('texts.credit_card') }}
|
||||
@endcomponent
|
||||
|
||||
@include('portal.ninja2020.gateways.includes.payment_details')
|
||||
|
||||
@component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.pay_with')])
|
||||
@if(count($tokens) > 0)
|
||||
@foreach($tokens as $token)
|
||||
<label class="mr-4">
|
||||
<input
|
||||
type="radio"
|
||||
data-token="{{ $token->token }}"
|
||||
name="payment-type"
|
||||
class="form-radio cursor-pointer toggle-payment-with-token"/>
|
||||
<span class="ml-1 cursor-pointer">**** {{ optional($token->meta)->last4 }}</span>
|
||||
</label>
|
||||
@endforeach
|
||||
@endisset
|
||||
|
||||
<label>
|
||||
<input
|
||||
type="radio"
|
||||
id="toggle-payment-with-credit-card"
|
||||
class="form-radio cursor-pointer"
|
||||
name="payment-type"
|
||||
checked/>
|
||||
<span class="ml-1 cursor-pointer">{{ __('texts.new_card') }}</span>
|
||||
</label>
|
||||
@endcomponent
|
||||
|
||||
@include('portal.ninja2020.gateways.includes.save_card')
|
||||
|
||||
@include('portal.ninja2020.gateways.wepay.includes.credit_card')
|
||||
|
||||
@include('portal.ninja2020.gateways.includes.pay_now')
|
||||
|
||||
</form>
|
||||
@endsection
|
||||
|
||||
@section('gateway_footer')
|
||||
<script>
|
||||
|
||||
|
||||
document.getElementById('pay-now').addEventListener('click', function() {
|
||||
document.getElementById('server_response').submit();
|
||||
});
|
||||
|
||||
</script>
|
||||
@endsection
|
||||
|
@ -198,6 +198,10 @@ Route::match(['get', 'post'], 'payment_webhook/{company_key}/{company_gateway_id
|
||||
->middleware(['guest'])
|
||||
->name('payment_webhook');
|
||||
|
||||
Route::match(['get', 'post'], 'payment_notification_webhook/{company_key}/{company_gateway_id}/{client}', 'PaymentNotificationWebhookController')
|
||||
->middleware(['guest'])
|
||||
->name('payment_notification_webhook');
|
||||
|
||||
Route::post('api/v1/postmark_webhook', 'PostMarkController@webhook');
|
||||
Route::get('token_hash_router', 'OneTimeTokenController@router');
|
||||
Route::get('webcron', 'WebCronController@index');
|
||||
|
67
tests/Unit/CentConversionTest.php
Normal file
67
tests/Unit/CentConversionTest.php
Normal file
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
namespace Tests\Unit;
|
||||
|
||||
use App\DataMapper\ClientSettings;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
class CentConversionTest extends TestCase
|
||||
{
|
||||
public function setUp() :void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
}
|
||||
|
||||
public function testConversionOfDecimalValues()
|
||||
{
|
||||
|
||||
$precision = 2;
|
||||
|
||||
$amount = 10.20;
|
||||
$amount = round(($amount * pow(10, $precision)),0);
|
||||
|
||||
$this->assertEquals(1020, $amount);
|
||||
|
||||
$amount = 2;
|
||||
$amount = round(($amount * pow(10, $precision)),0);
|
||||
|
||||
$this->assertEquals(200, $amount);
|
||||
|
||||
$amount = 2.12;
|
||||
$amount = round(($amount * pow(10, $precision)),0);
|
||||
|
||||
$this->assertEquals(212, $amount);
|
||||
}
|
||||
|
||||
|
||||
public function testBcMathWay()
|
||||
{
|
||||
|
||||
$amount = 64.99;
|
||||
$amount = bcmul($amount, 100);
|
||||
|
||||
$this->assertEquals(6499, $amount);
|
||||
|
||||
$amount = 2;
|
||||
$amount = bcmul($amount, 100);
|
||||
|
||||
$this->assertEquals(200, $amount);
|
||||
|
||||
$amount = 2.12;
|
||||
$amount = bcmul($amount, 100);
|
||||
|
||||
$this->assertEquals(212, $amount);
|
||||
}
|
||||
}
|
@ -16,6 +16,7 @@ use App\Models\Company;
|
||||
use App\Models\Credit;
|
||||
use App\Models\CreditInvitation;
|
||||
use App\Models\User;
|
||||
use App\Utils\Traits\AppSetup;
|
||||
use Tests\MockUnitData;
|
||||
use Tests\TestCase;
|
||||
|
||||
@ -25,6 +26,7 @@ use Tests\TestCase;
|
||||
class CreditBalanceTest extends TestCase
|
||||
{
|
||||
use MockUnitData;
|
||||
use AppSetup;
|
||||
|
||||
public function setUp() :void
|
||||
{
|
||||
@ -35,6 +37,8 @@ class CreditBalanceTest extends TestCase
|
||||
});
|
||||
|
||||
$this->makeTestData();
|
||||
|
||||
$this->buildCache(true);
|
||||
}
|
||||
|
||||
public function testCreditBalance()
|
||||
|
Loading…
Reference in New Issue
Block a user