diff --git a/app/Models/GatewayType.php b/app/Models/GatewayType.php index f87ee10df145..e3e6c5b26a58 100644 --- a/app/Models/GatewayType.php +++ b/app/Models/GatewayType.php @@ -37,4 +37,9 @@ class GatewayType extends StaticModel { return $this->belongsTo(Gateway::class); } + + public function payment_methods() + { + return $this->hasMany(PaymentType::class); + } } diff --git a/app/PaymentDrivers/Authorize/AuthorizeCreditCard.php b/app/PaymentDrivers/Authorize/AuthorizeCreditCard.php index f961857f148b..2105ca383d5f 100644 --- a/app/PaymentDrivers/Authorize/AuthorizeCreditCard.php +++ b/app/PaymentDrivers/Authorize/AuthorizeCreditCard.php @@ -209,7 +209,6 @@ class AuthorizeCreditCard 'code' => $response->getTransactionResponse()->getMessages()[0]->getCode(), 'description' => $response->getTransactionResponse()->getMessages()[0]->getDescription(), 'invoices' => $vars['hashed_ids'], - ]; } diff --git a/app/PaymentDrivers/AuthorizePaymentDriver.php b/app/PaymentDrivers/AuthorizePaymentDriver.php index 079dbc7a5852..f0644e642c07 100644 --- a/app/PaymentDrivers/AuthorizePaymentDriver.php +++ b/app/PaymentDrivers/AuthorizePaymentDriver.php @@ -37,6 +37,10 @@ class AuthorizePaymentDriver extends BaseDriver public $merchant_authentication; + public $token_billing = true; + + public $can_authorise_credit_card = true; + public static $methods = [ GatewayType::CREDIT_CARD => AuthorizeCreditCard::class, ]; diff --git a/app/PaymentDrivers/Stripe/Charge.php b/app/PaymentDrivers/Stripe/Charge.php index f44bbebb46d8..38aa4d0b3d79 100644 --- a/app/PaymentDrivers/Stripe/Charge.php +++ b/app/PaymentDrivers/Stripe/Charge.php @@ -12,8 +12,14 @@ namespace App\PaymentDrivers\Stripe; +use App\Events\Payment\PaymentWasCreated; +use App\Jobs\Util\SystemLogger; use App\Models\ClientGatewayToken; +use App\Models\Invoice; +use App\Models\PaymentType; +use App\Models\SystemLog; use App\PaymentDrivers\StripePaymentDriver; +use App\Utils\Ninja; class Charge { @@ -37,18 +43,199 @@ class Charge else $description = "Payment with no invoice for amount {$amount} for client {$this->stripe->client->present()->name()}"; - $response = $this->stripe->charges->create([ - 'amount' => $this->stripe->convertToStripeAmount($amount, $this->stripe->client->currency()->precision), - 'currency' => $this->stripe->client->getCurrencyCode(), - 'source' => $cgt->token, - 'description' => $description, - ]); + $this->stripe->init(); - info(print_r($response,1)); + $local_stripe = new \Stripe\StripeClient( + $this->stripe->company_gateway->getConfigField('apiKey') + ); + + $response = null; + + try { + + $response = $local_stripe->paymentIntents->create([ + 'amount' => $this->stripe->convertToStripeAmount($amount, $this->stripe->client->currency()->precision), + 'currency' => $this->stripe->client->getCurrencyCode(), + 'payment_method' => $cgt->token, + 'customer' => $cgt->gateway_customer_reference, + 'confirm' => true, + 'description' => $description, + ]); + + SystemLogger::dispatch($response, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_SUCCESS, SystemLog::TYPE_STRIPE, $this->stripe->client); + + + } catch(\Stripe\Exception\CardException $e) { + // Since it's a decline, \Stripe\Exception\CardException will be caught + + $data = [ + 'status' => $e->getHttpStatus(), + 'error_type' => $e->getError()->type, + 'error_code' => $e->getError()->code, + 'param' => $e->getError()->param, + 'message' => $e->getError()->message + ]; + + SystemLogger::dispatch($data, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_STRIPE, $this->stripe->client); + + } catch (\Stripe\Exception\RateLimitException $e) { + // Too many requests made to the API too quickly + + $data = [ + 'status' => '', + 'error_type' => '', + 'error_code' => '', + 'param' => '', + 'message' => 'Too many requests made to the API too quickly' + ]; + + SystemLogger::dispatch($data, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_STRIPE, $this->stripe->client); + + } catch (\Stripe\Exception\InvalidRequestException $e) { + // Invalid parameters were supplied to Stripe's API + // + $data = [ + 'status' => '', + 'error_type' => '', + 'error_code' => '', + 'param' => '', + 'message' => 'Invalid parameters were supplied to Stripe\'s API' + ]; + + SystemLogger::dispatch($data, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_STRIPE, $this->stripe->client); + + } catch (\Stripe\Exception\AuthenticationException $e) { + // Authentication with Stripe's API failed + + $data = [ + 'status' => '', + 'error_type' => '', + 'error_code' => '', + 'param' => '', + 'message' => 'Authentication with Stripe\'s API failed' + ]; + + SystemLogger::dispatch($data, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_STRIPE, $this->stripe->client); + + } catch (\Stripe\Exception\ApiConnectionException $e) { + // Network communication with Stripe failed + + $data = [ + 'status' => '', + 'error_type' => '', + 'error_code' => '', + 'param' => '', + 'message' => 'Network communication with Stripe failed' + ]; + + SystemLogger::dispatch($data, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_STRIPE, $this->stripe->client); + + } catch (\Stripe\Exception\ApiErrorException $e) { + + $data = [ + 'status' => '', + 'error_type' => '', + 'error_code' => '', + 'param' => '', + 'message' => 'API Error' + ]; + + SystemLogger::dispatch($data, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_STRIPE, $this->stripe->client); + + } catch (Exception $e) { + // Something else happened, completely unrelated to Stripe + // + $data = [ + 'status' => '', + 'error_type' => '', + 'error_code' => '', + 'param' => '', + 'message' => $e->getMessage(), + ]; + + SystemLogger::dispatch($data, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_STRIPE, $this->stripe->client); + } + + if(!$response) + return false; + + $payment_method_type = $response->charges->data[0]->payment_method_details->card->brand; + info($payment_method_type); + + $data = [ + 'gateway_type_id' => $cgt->gateway_type_id, + 'type_id' => $this->transformPaymentTypeToConstant($payment_method_type), + 'transaction_reference' => $response->charges->data[0]->id, + ]; + + $payment = $this->stripe->createPaymentRecord($data, $amount); + + if($invoice) + $this->stripe->attachInvoices($payment, $invoice->hashed_id); + + $payment->service()->updateInvoicePayment(); + + event(new PaymentWasCreated($payment, $payment->company, Ninja::eventVars())); + + return $payment; + } + + + private function formatGatewayResponse($data, $vars) + { + $response = $data['response']; + + return [ + 'transaction_reference' => $response->getTransactionResponse()->getTransId(), + 'amount' => $vars['amount'], + 'auth_code' => $response->getTransactionResponse()->getAuthCode(), + 'code' => $response->getTransactionResponse()->getMessages()[0]->getCode(), + 'description' => $response->getTransactionResponse()->getMessages()[0]->getDescription(), + 'invoices' => $vars['hashed_ids'], + ]; + } + + private function transformPaymentTypeToConstant($type) + { + switch ($type) { + case 'visa': + return PaymentType::VISA; + break; + case 'mastercard': + return PaymentType::MASTERCARD; + break; + default: + return PaymentType::CREDIT_CARD_OTHER; + break; + } } } + // const CREDIT = 1; + // const ACH = 4; + // const VISA = 5; + // const MASTERCARD = 6; + // const AMERICAN_EXPRESS = 7; + // const DISCOVER = 8; + // const DINERS = 9; + // const EUROCARD = 10; + // const NOVA = 11; + // const CREDIT_CARD_OTHER = 12; + // const PAYPAL = 13; + // const CARTE_BLANCHE = 16; + // const UNIONPAY = 17; + // const JCB = 18; + // const LASER = 19; + // const MAESTRO = 20; + // const SOLO = 21; + // const SWITCH = 22; + // const ALIPAY = 27; + // const SOFORT = 28; + // const SEPA = 29; + // const GOCARDLESS = 30; + // const CRYPTO = 31; + // { // "id": "ch_1H4lp42eZvKYlo2Ch5igaUwg", // "object": "charge", @@ -130,4 +317,196 @@ class Charge // "transfer_data": null, // "transfer_group": null, // "source": "tok_visa" -// } \ No newline at end of file +// } +// +// +// [2020-07-14 23:06:47] local.INFO: Stripe\PaymentIntent Object +// ( +// [id] => pi_1H4xD0Kmol8YQE9DKhrvV6Nc +// [object] => payment_intent +// [allowed_source_types] => Array +// ( +// [0] => card +// ) + +// [amount] => 1000 +// [amount_capturable] => 0 +// [amount_received] => 1000 +// [application] => +// [application_fee_amount] => +// [canceled_at] => +// [cancellation_reason] => +// [capture_method] => automatic +// [charges] => Stripe\Collection Object +// ( +// [object] => list +// [data] => Array +// ( +// [0] => Stripe\Charge Object +// ( +// [id] => ch_1H4xD0Kmol8YQE9Ds9b1ZWjw +// [object] => charge +// [amount] => 1000 +// [amount_refunded] => 0 +// [application] => +// [application_fee] => +// [application_fee_amount] => +// [balance_transaction] => txn_1H4xD1Kmol8YQE9DE9qFoO0R +// [billing_details] => Stripe\StripeObject Object +// ( +// [address] => Stripe\StripeObject Object +// ( +// [city] => +// [country] => +// [line1] => +// [line2] => +// [postal_code] => 42334 +// [state] => +// ) + +// [email] => +// [name] => sds +// [phone] => +// ) + +// [calculated_statement_descriptor] => NODDY +// [captured] => 1 +// [created] => 1594768006 +// [currency] => usd +// [customer] => cus_He4VEiYldHJWqG +// [description] => Invoice 0023 for 10 for client Corwin Group +// [destination] => +// [dispute] => +// [disputed] => +// [failure_code] => +// [failure_message] => +// [fraud_details] => Array +// ( +// ) + +// [invoice] => +// [livemode] => +// [metadata] => Stripe\StripeObject Object +// ( +// ) + +// [on_behalf_of] => +// [order] => +// [outcome] => Stripe\StripeObject Object +// ( +// [network_status] => approved_by_network +// [reason] => +// [risk_level] => normal +// [risk_score] => 13 +// [seller_message] => Payment complete. +// [type] => authorized +// ) + +// [paid] => 1 +// [payment_intent] => pi_1H4xD0Kmol8YQE9DKhrvV6Nc +// [payment_method] => pm_1H4mNAKmol8YQE9DUMRsuTXs +// [payment_method_details] => Stripe\StripeObject Object +// ( +// [card] => Stripe\StripeObject Object +// ( +// [brand] => visa +// [checks] => Stripe\StripeObject Object +// ( +// [address_line1_check] => +// [address_postal_code_check] => pass +// [cvc_check] => +// ) + +// [country] => US +// [exp_month] => 4 +// [exp_year] => 2024 +// [fingerprint] => oCjEXlb4syFKwgbJ +// [funding] => credit +// [installments] => +// [last4] => 4242 +// [network] => visa +// [three_d_secure] => +// [wallet] => +// ) + +// [type] => card +// ) + +// [receipt_email] => +// [receipt_number] => +// [receipt_url] => https://pay.stripe.com/receipts/acct_19DXXPKmol8YQE9D/ch_1H4xD0Kmol8YQE9Ds9b1ZWjw/rcpt_HeFiiwzRZtnOpvHyohNN5JXtCYe8Rdc +// [refunded] => +// [refunds] => Stripe\Collection Object +// ( +// [object] => list +// [data] => Array +// ( +// ) + +// [has_more] => +// [total_count] => 0 +// [url] => /v1/charges/ch_1H4xD0Kmol8YQE9Ds9b1ZWjw/refunds +// ) + +// [review] => +// [shipping] => +// [source] => +// [source_transfer] => +// [statement_descriptor] => +// [statement_descriptor_suffix] => +// [status] => succeeded +// [transfer_data] => +// [transfer_group] => +// ) + +// ) + +// [has_more] => +// [total_count] => 1 +// [url] => /v1/charges?payment_intent=pi_1H4xD0Kmol8YQE9DKhrvV6Nc +// ) + +// [client_secret] => pi_1H4xD0Kmol8YQE9DKhrvV6Nc_secret_TyE8n3Y3oaMqgqQvXvtKDOnYT +// [confirmation_method] => automatic +// [created] => 1594768006 +// [currency] => usd +// [customer] => cus_He4VEiYldHJWqG +// [description] => Invoice 0023 for 10 for client Corwin Group +// [invoice] => +// [last_payment_error] => +// [livemode] => +// [metadata] => Stripe\StripeObject Object +// ( +// ) + +// [next_action] => +// [next_source_action] => +// [on_behalf_of] => +// [payment_method] => pm_1H4mNAKmol8YQE9DUMRsuTXs +// [payment_method_options] => Stripe\StripeObject Object +// ( +// [card] => Stripe\StripeObject Object +// ( +// [installments] => +// [network] => +// [request_three_d_secure] => automatic +// ) + +// ) + +// [payment_method_types] => Array +// ( +// [0] => card +// ) + +// [receipt_email] => +// [review] => +// [setup_future_usage] => +// [shipping] => +// [source] => +// [statement_descriptor] => +// [statement_descriptor_suffix] => +// [status] => succeeded +// [transfer_data] => +// [transfer_group] => +// ) \ No newline at end of file diff --git a/app/PaymentDrivers/StripePaymentDriver.php b/app/PaymentDrivers/StripePaymentDriver.php index b27967dae0fc..523d9a894df3 100644 --- a/app/PaymentDrivers/StripePaymentDriver.php +++ b/app/PaymentDrivers/StripePaymentDriver.php @@ -42,11 +42,11 @@ class StripePaymentDriver extends BasePaymentDriver { use MakesHash, Utilities; - protected $refundable = true; + public $refundable = true; - protected $token_billing = true; + public $token_billing = true; - protected $can_authorise_credit_card = true; + public $can_authorise_credit_card = true; protected $customer_reference = 'customerReferenceParam'; @@ -369,8 +369,33 @@ class StripePaymentDriver extends BasePaymentDriver public function tokenBilling(ClientGatewayToken $cgt, float $amount, ?Invoice $invoice = null) { - return (new Charge)->tokenBilling($cgt, $amount, $invoice); + return (new Charge($this))->tokenBilling($cgt, $amount, $invoice); } + /** + * Creates a payment record for the given + * data array. + * + * @param array $data An array of payment attributes + * @param float $amount The amount of the payment + * @return Payment The payment object + */ + public function createPaymentRecord($data, $amount) :?Payment + { + $payment = PaymentFactory::create($this->client->company_id, $this->client->user_id); + $payment->client_id = $this->client->id; + $payment->company_gateway_id = $this->company_gateway->id; + $payment->status_id = Payment::STATUS_COMPLETED; + $payment->gateway_type_id = $data['gateway_type_id']; + $payment->type_id = $data['type_id']; + $payment->currency_id = $this->client->getSetting('currency_id'); + $payment->date = Carbon::now(); + $payment->transaction_reference = $data['transaction_reference']; + $payment->amount = $amount; + $payment->client->getNextPaymentNumber($this->client); + $payment->save(); + + return $payment; + } } diff --git a/app/Services/Invoice/AutoBillInvoice.php b/app/Services/Invoice/AutoBillInvoice.php index e82a122a8000..cc8ec5f8d66b 100644 --- a/app/Services/Invoice/AutoBillInvoice.php +++ b/app/Services/Invoice/AutoBillInvoice.php @@ -48,11 +48,10 @@ class AutoBillInvoice extends AbstractService return $this->invoice->service()->markPaid()->save(); if(!$gateway_token || !$gateway_token->gateway->driver($this->client)->token_billing){ - info("either no gateway token record OR tokenbilling not implemented"); return $this->invoice; } - if($this->invoice->partial){ + if($this->invoice->partial > 0){ $fee = $gateway_token->gateway->calcGatewayFee($this->invoice->partial); $amount = $this->invoice->partial + $fee; } @@ -67,29 +66,39 @@ class AutoBillInvoice extends AbstractService if($fee > 0) $this->addFeeToInvoice($fee); - $response = $gateway_token->gateway->driver($this->client)->tokenBilling($gateway_token, $amount, $this->invoice); + $payment = $gateway_token->gateway->driver($this->client)->tokenBilling($gateway_token, $amount, $this->invoice); - //if response was successful, toggle the fee type_id to paid + if($payment){ + + $this->invoice->service()->toggleFeesPaid()->save(); + + } + else + { + //autobill failed + } + + + return $this->invoice; } + /** + * Harvests a client gateway token which passes the + * necessary filters for an $amount + * + * @param float $amount The amount to charge + * @return ClientGatewayToken The client gateway token + */ private function getGateway($amount) { - // $gateway_tokens = $this->client->gateway_tokens()->orderBy('is_default', 'DESC'); - - // return $gateway_tokens->filter(function ($token) use ($amount){ - - // return $this->validGatewayLimits($token, $amount); - - // })->all()->first(); - - $gateway_tokens = $this->client->gateway_tokens()->orderBy('is_default', 'DESC')->get(); foreach($gateway_tokens as $gateway_token) { - if($this->validGatewayLimits($gateway_token, $amount)) + if($this->validGatewayLimits($gateway_token, $amount)){ return $gateway_token; + } } } @@ -152,7 +161,7 @@ class AutoBillInvoice extends AbstractService $this->invoice->line_items = $new_items; $this->invoice->save(); - $this->invoice = $this->invoice->calc()->getInvoice()->save(); + $this->invoice = $this->invoice->calc()->getInvoice(); if($starting_amount != $this->invoice->amount && $this->invoice->status_id != Invoice::STATUS_DRAFT){ $this->invoice->client->service()->updateBalance($this->invoice->amount - $starting_amount)->save(); @@ -176,7 +185,7 @@ class AutoBillInvoice extends AbstractService if(isset($cg->fees_and_limits)) $fees_and_limits = $cg->fees_and_limits->{"1"}; else - $passes = true; + return true; if ((property_exists($fees_and_limits, 'min_limit')) && $fees_and_limits->min_limit !== null && $amount < $fees_and_limits->min_limit) { info("amount {$amount} less than ". $fees_and_limits->min_limit); diff --git a/app/Services/Invoice/InvoiceService.php b/app/Services/Invoice/InvoiceService.php index c1999ec42edb..40d587733b2a 100644 --- a/app/Services/Invoice/InvoiceService.php +++ b/app/Services/Invoice/InvoiceService.php @@ -178,6 +178,33 @@ class InvoiceService return $this; } + public function toggleFeesPaid() + { + + $this->invoice->line_items = collect($this->invoice->line_items) + ->where('type_id',3)->map(function ($item) { + + $item->type_id=4; + return $item; + + })->toArray(); + + return $this; + } + + public function removeUnpaidGatewayFees() + { + + $this->invoice->line_items = collect($this->invoice->line_items) + ->reject(function ($item) { + + return $item->type_id == 3; + + })->toArray(); + + return $this; + } + public function clearPartial() { $this->invoice->partial = null;