1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-13 22:54:25 +01:00

Fixes for User model

This commit is contained in:
David Bomba 2021-07-10 18:59:55 +10:00
commit 6edfa0a966
21 changed files with 1140 additions and 5 deletions

View File

@ -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);
}
}

View File

@ -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 [
//
];
}
}

View File

@ -71,7 +71,6 @@ class PaymentNotification implements ShouldQueue
} }
/*Google Analytics Track Revenue*/ /*Google Analytics Track Revenue*/
if (isset($payment->company->google_analytics_key)) { if (isset($payment->company->google_analytics_key)) {
$this->trackRevenue($event); $this->trackRevenue($event);

View File

@ -371,6 +371,11 @@ class CompanyGateway extends BaseModel
return $fee; 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 * we need to average out the gateway fees across all the invoices
* so lets iterate. * so lets iterate.
@ -412,4 +417,6 @@ class CompanyGateway extends BaseModel
return $this return $this
->where('id', $this->decodePrimaryKey($value))->firstOrFail(); ->where('id', $this->decodePrimaryKey($value))->firstOrFail();
} }
} }

View File

@ -67,6 +67,7 @@ class SystemLog extends Model
const TYPE_CUSTOM = 306; const TYPE_CUSTOM = 306;
const TYPE_BRAINTREE = 307; const TYPE_BRAINTREE = 307;
const TYPE_WEPAY = 309; const TYPE_WEPAY = 309;
const TYPE_PAYFAST = 310;
const TYPE_QUOTA_EXCEEDED = 400; const TYPE_QUOTA_EXCEEDED = 400;

View File

@ -161,7 +161,7 @@ class User extends Authenticatable implements MustVerifyEmail
public function setCompany($company) public function setCompany($company)
{ {
$this->company = $company; $this->company = $company;
return $this; return $this;
} }

View File

@ -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*/ /* Performs an extra iterate on the gatewayTypes() array and passes back only the enabled gateways*/
public function gatewayTypeEnabled($type) public function gatewayTypeEnabled($type)
{ {

View File

@ -11,6 +11,7 @@
namespace App\PaymentDrivers; namespace App\PaymentDrivers;
use App\Http\Requests\Payments\PaymentWebhookRequest;
use App\Models\ClientGatewayToken; use App\Models\ClientGatewayToken;
use App\Models\GatewayType; use App\Models\GatewayType;
use App\Models\Payment; 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 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) public function setPaymentMethod($payment_method_id)
{ {
$class = self::$methods[$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 return $this->payment_method->yourTokenBillingImplmentation(); //this is your custom implementation from here
} }
public function processWebhookRequest(PaymentWebhookRequest $request, Payment $payment = null)
{
}
} }

View 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);
}
}

View 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 merchants 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());
}
}
}

View 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);
}
}

View File

@ -117,7 +117,6 @@ use WePayCommon;
nlog("authorize the card first!"); nlog("authorize the card first!");
$response = $this->wepay_payment_driver->wepay->request('credit_card/authorize', array( $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_id' => config('ninja.wepay.client_id'),
'client_secret' => config('ninja.wepay.client_secret'), 'client_secret' => config('ninja.wepay.client_secret'),
'credit_card_id' => (int)$request->input('credit_card_id'), 'credit_card_id' => (int)$request->input('credit_card_id'),

View File

@ -26,9 +26,9 @@ use App\Utils\CurlUtils;
use App\Utils\HtmlEngine; use App\Utils\HtmlEngine;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Response; use Illuminate\Support\Facades\Response;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Str; use Illuminate\Support\Str;
class Phantom class Phantom

View File

@ -64,6 +64,7 @@
"maennchen/zipstream-php": "^1.2", "maennchen/zipstream-php": "^1.2",
"nwidart/laravel-modules": "^8.0", "nwidart/laravel-modules": "^8.0",
"omnipay/paypal": "^3.0", "omnipay/paypal": "^3.0",
"payfast/payfast-php-sdk": "^1.1",
"pragmarx/google2fa": "^8.0", "pragmarx/google2fa": "^8.0",
"predis/predis": "^1.1", "predis/predis": "^1.1",
"sentry/sentry-laravel": "^2", "sentry/sentry-laravel": "^2",

131
composer.lock generated
View File

@ -4,7 +4,11 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
<<<<<<< HEAD
"content-hash": "16a38ffa3774d9d28a9f4c49366baac0", "content-hash": "16a38ffa3774d9d28a9f4c49366baac0",
=======
"content-hash": "26f6cde504d8ed4276289151e695250e",
>>>>>>> payfast
"packages": [ "packages": [
{ {
"name": "asm/php-ansible", "name": "asm/php-ansible",
@ -159,6 +163,7 @@
}, },
{ {
"name": "aws/aws-sdk-php", "name": "aws/aws-sdk-php",
<<<<<<< HEAD
"version": "3.185.7", "version": "3.185.7",
"source": { "source": {
"type": "git", "type": "git",
@ -169,6 +174,18 @@
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/7c0cd260e749374b5df247c4768c8f33f9a604e4", "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/7c0cd260e749374b5df247c4768c8f33f9a604e4",
"reference": "7c0cd260e749374b5df247c4768c8f33f9a604e4", "reference": "7c0cd260e749374b5df247c4768c8f33f9a604e4",
=======
"version": "3.185.6",
"source": {
"type": "git",
"url": "https://github.com/aws/aws-sdk-php.git",
"reference": "35310302912fdc3b4a0e829b84424c41e3fd9727"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/35310302912fdc3b4a0e829b84424c41e3fd9727",
"reference": "35310302912fdc3b4a0e829b84424c41e3fd9727",
>>>>>>> payfast
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -243,9 +260,15 @@
"support": { "support": {
"forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80", "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
"issues": "https://github.com/aws/aws-sdk-php/issues", "issues": "https://github.com/aws/aws-sdk-php/issues",
<<<<<<< HEAD
"source": "https://github.com/aws/aws-sdk-php/tree/3.185.7" "source": "https://github.com/aws/aws-sdk-php/tree/3.185.7"
}, },
"time": "2021-07-06T18:16:14+00:00" "time": "2021-07-06T18:16:14+00:00"
=======
"source": "https://github.com/aws/aws-sdk-php/tree/3.185.6"
},
"time": "2021-07-02T18:13:18+00:00"
>>>>>>> payfast
}, },
{ {
"name": "bacon/bacon-qr-code", "name": "bacon/bacon-qr-code",
@ -2292,6 +2315,7 @@
}, },
{ {
"name": "google/apiclient-services", "name": "google/apiclient-services",
<<<<<<< HEAD
"version": "v0.202.0", "version": "v0.202.0",
"source": { "source": {
"type": "git", "type": "git",
@ -2302,6 +2326,18 @@
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/c72cb04fde47a2cc3336c1f513f9ff5db27ccc35", "url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/c72cb04fde47a2cc3336c1f513f9ff5db27ccc35",
"reference": "c72cb04fde47a2cc3336c1f513f9ff5db27ccc35", "reference": "c72cb04fde47a2cc3336c1f513f9ff5db27ccc35",
=======
"version": "v0.201.1",
"source": {
"type": "git",
"url": "https://github.com/googleapis/google-api-php-client-services.git",
"reference": "ee1072221acf7c32e3de9b18e11fec3ab23ec38f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/ee1072221acf7c32e3de9b18e11fec3ab23ec38f",
"reference": "ee1072221acf7c32e3de9b18e11fec3ab23ec38f",
>>>>>>> payfast
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -2330,9 +2366,15 @@
], ],
"support": { "support": {
"issues": "https://github.com/googleapis/google-api-php-client-services/issues", "issues": "https://github.com/googleapis/google-api-php-client-services/issues",
<<<<<<< HEAD
"source": "https://github.com/googleapis/google-api-php-client-services/tree/v0.202.0" "source": "https://github.com/googleapis/google-api-php-client-services/tree/v0.202.0"
}, },
"time": "2021-07-03T11:20:23+00:00" "time": "2021-07-03T11:20:23+00:00"
=======
"source": "https://github.com/googleapis/google-api-php-client-services/tree/v0.201.1"
},
"time": "2021-06-27T11:20:22+00:00"
>>>>>>> payfast
}, },
{ {
"name": "google/auth", "name": "google/auth",
@ -3184,6 +3226,7 @@
}, },
{ {
"name": "laravel/framework", "name": "laravel/framework",
<<<<<<< HEAD
"version": "v8.49.2", "version": "v8.49.2",
"source": { "source": {
"type": "git", "type": "git",
@ -3194,6 +3237,18 @@
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/d9b43ee080b4d51344b2e578aa667f85040471a2", "url": "https://api.github.com/repos/laravel/framework/zipball/d9b43ee080b4d51344b2e578aa667f85040471a2",
"reference": "d9b43ee080b4d51344b2e578aa667f85040471a2", "reference": "d9b43ee080b4d51344b2e578aa667f85040471a2",
=======
"version": "v8.49.1",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
"reference": "62aee1bfeefd82f160c7aa3b4c63cb2f053215c0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/62aee1bfeefd82f160c7aa3b4c63cb2f053215c0",
"reference": "62aee1bfeefd82f160c7aa3b4c63cb2f053215c0",
>>>>>>> payfast
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -3348,7 +3403,11 @@
"issues": "https://github.com/laravel/framework/issues", "issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework" "source": "https://github.com/laravel/framework"
}, },
<<<<<<< HEAD
"time": "2021-07-06T14:06:38+00:00" "time": "2021-07-06T14:06:38+00:00"
=======
"time": "2021-07-02T16:50:12+00:00"
>>>>>>> payfast
}, },
{ {
"name": "laravel/slack-notification-channel", "name": "laravel/slack-notification-channel",
@ -4631,6 +4690,7 @@
}, },
{ {
"name": "myclabs/php-enum", "name": "myclabs/php-enum",
<<<<<<< HEAD
"version": "1.8.3", "version": "1.8.3",
"source": { "source": {
"type": "git", "type": "git",
@ -4641,6 +4701,18 @@
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/myclabs/php-enum/zipball/b942d263c641ddb5190929ff840c68f78713e937", "url": "https://api.github.com/repos/myclabs/php-enum/zipball/b942d263c641ddb5190929ff840c68f78713e937",
"reference": "b942d263c641ddb5190929ff840c68f78713e937", "reference": "b942d263c641ddb5190929ff840c68f78713e937",
=======
"version": "1.8.2",
"source": {
"type": "git",
"url": "https://github.com/myclabs/php-enum.git",
"reference": "8bef486e885cae67ced6e43257300e8acc3f06ad"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/myclabs/php-enum/zipball/8bef486e885cae67ced6e43257300e8acc3f06ad",
"reference": "8bef486e885cae67ced6e43257300e8acc3f06ad",
>>>>>>> payfast
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -4675,7 +4747,11 @@
], ],
"support": { "support": {
"issues": "https://github.com/myclabs/php-enum/issues", "issues": "https://github.com/myclabs/php-enum/issues",
<<<<<<< HEAD
"source": "https://github.com/myclabs/php-enum/tree/1.8.3" "source": "https://github.com/myclabs/php-enum/tree/1.8.3"
=======
"source": "https://github.com/myclabs/php-enum/tree/1.8.2"
>>>>>>> payfast
}, },
"funding": [ "funding": [
{ {
@ -4687,7 +4763,11 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
<<<<<<< HEAD
"time": "2021-07-05T08:18:36+00:00" "time": "2021-07-05T08:18:36+00:00"
=======
"time": "2021-07-04T17:44:39+00:00"
>>>>>>> payfast
}, },
{ {
"name": "nesbot/carbon", "name": "nesbot/carbon",
@ -5334,6 +5414,57 @@
}, },
"time": "2020-10-15T08:29:30+00:00" "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", "name": "php-http/client-common",
"version": "2.4.0", "version": "2.4.0",

View File

@ -109,7 +109,6 @@ return [
'gelf' => [ 'gelf' => [
'driver' => 'custom', 'driver' => 'custom',
'via' => \Hedii\LaravelGelfLogger\GelfLoggerFactory::class, 'via' => \Hedii\LaravelGelfLogger\GelfLoggerFactory::class,
// This optional option determines the processors that should be // This optional option determines the processors that should be

View File

@ -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()
{
//
}
}

View File

@ -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

View File

@ -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

View File

@ -198,6 +198,10 @@ Route::match(['get', 'post'], 'payment_webhook/{company_key}/{company_gateway_id
->middleware(['guest']) ->middleware(['guest'])
->name('payment_webhook'); ->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::post('api/v1/postmark_webhook', 'PostMarkController@webhook');
Route::get('token_hash_router', 'OneTimeTokenController@router'); Route::get('token_hash_router', 'OneTimeTokenController@router');
Route::get('webcron', 'WebCronController@index'); Route::get('webcron', 'WebCronController@index');

View 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);
}
}