diff --git a/app/Models/Client.php b/app/Models/Client.php index 4993a78ced45..0d675add42f9 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -24,6 +24,7 @@ use App\Models\Filterable; use App\Models\GatewayType; use App\Models\GroupSetting; use App\Models\Timezone; +use App\Models\User; use App\Utils\Traits\CompanyGatewaySettings; use App\Utils\Traits\GeneratesCounter; use App\Utils\Traits\MakesDates; @@ -137,6 +138,11 @@ class Client extends BaseModel return $this->belongsTo(Company::class); } + public function user() + { + return $this->belongsTo(User::class); + } + public function country() { return $this->belongsTo(Country::class); diff --git a/app/Models/PaymentType.php b/app/Models/PaymentType.php index d1384d665b21..aa54e850a177 100644 --- a/app/Models/PaymentType.php +++ b/app/Models/PaymentType.php @@ -19,4 +19,61 @@ class PaymentType extends StaticModel * @var bool */ public $timestamps = false; + + const CREDIT = 1; + const ACH = 5; + const VISA = 6; + const MASTERCARD = 7; + const AMERICAN_EXPRESS = 8; + const DISCOVER = 9; + const DINERS = 10; + const EUROCARD = 11; + const NOVA = 12; + const CREDIT_CARD_OTHER = 13; + const PAYPAL = 14; + const CARTE_BLANCHE = 17; + const UNIONPAY = 18; + const JCB = 19; + const LASER = 20; + const MAESTRO = 21; + const SOLO = 22; + const SWITCH = 23; + const ALIPAY = 28; + const SOFORT = 29; + const SEPA = 30; + const GOCARDLESS = 31; + const BITCOIN = 32; + + public static function parseCardType($cardName) + { + $cardTypes = [ + 'visa' => self::VISA, + 'americanexpress' => self::AMERICAN_EXPRESS, + 'amex' => self::AMERICAN_EXPRESS, + 'mastercard' => self::MASTERCARD, + 'discover' => self::DISCOVER, + 'jcb' => self::JCB, + 'dinersclub' => self::DINERS, + 'carteblanche' => self::CARTE_BLANCHE, + 'chinaunionpay' => self::UNIONPAY, + 'unionpay' => self::UNIONPAY, + 'laser' => self::LASER, + 'maestro' => self::MAESTRO, + 'solo' => self::SOLO, + 'switch' => self::SWITCH, + ]; + + $cardName = strtolower(str_replace([' ', '-', '_'], '', $cardName)); + + if (empty($cardTypes[$cardName]) && 1 == preg_match('/^('.implode('|', array_keys($cardTypes)).')/', $cardName, $matches)) { + // Some gateways return extra stuff after the card name + $cardName = $matches[1]; + } + + if (! empty($cardTypes[$cardName])) { + return $cardTypes[$cardName]; + } else { + return self::CREDIT_CARD_OTHER; + } + } } diff --git a/app/PaymentDrivers/BasePaymentDriver.php b/app/PaymentDrivers/BasePaymentDriver.php index 7d984d95d0c8..ec0cba1f51ad 100644 --- a/app/PaymentDrivers/BasePaymentDriver.php +++ b/app/PaymentDrivers/BasePaymentDriver.php @@ -12,8 +12,10 @@ namespace App\PaymentDrivers; use App\Models\Client; +use App\Models\ClientContact; use App\Models\CompanyGateway; use App\Models\GatewayType; +use Illuminate\Support\Facades\Auth; use Omnipay\Omnipay; @@ -123,6 +125,16 @@ class BasePaymentDriver public function processPaymentResponse($request) {} + public function getContact() + { + if($this->invitation) + return ClientContact::find($this->invitation->client_contact_id); + elseif(auth()->guard('contact')->user()) + return auth()->user(); + else + return false; + } + /************************************* Omnipay ****************************************** authorize($options) - authorize an amount on the customer's card completeAuthorize($options) - handle return from off-site gateways after authorization diff --git a/app/PaymentDrivers/StripePaymentDriver.php b/app/PaymentDrivers/StripePaymentDriver.php index a28a42171668..083dbeb564ca 100644 --- a/app/PaymentDrivers/StripePaymentDriver.php +++ b/app/PaymentDrivers/StripePaymentDriver.php @@ -11,34 +11,31 @@ namespace App\PaymentDrivers; +use App\Factory\PaymentFactory; use App\Models\ClientGatewayToken; use App\Models\GatewayType; +use App\Models\Invoice; +use App\Models\Payment; +use App\Models\PaymentType; +use App\Utils\Traits\MakesHash; +use Illuminate\Http\Request; +use Illuminate\Support\Carbon; use Stripe\PaymentIntent; use Stripe\SetupIntent; use Stripe\Stripe; class StripePaymentDriver extends BasePaymentDriver { + use MakesHash; + protected $refundable = true; protected $token_billing = true; - protected $can_authorise_credit_card = true; + protected $can_authorise_credit_card = true; protected $customer_reference = 'customerReferenceParam'; -/** - * Payments - * \Stripe\PaymentIntent::create([ - 'payment_method_types' => ['card'], - 'amount' => 1099, - 'currency' => 'aud', - 'customer' => 'cus_Fow2nmVJX1EsQw', - 'payment_method' => 'card_1FJIAjKmol8YQE9DxWb9kMpR', -]); - */ - - /** * Methods in this class are divided into * two separate streams @@ -128,6 +125,14 @@ class StripePaymentDriver extends BasePaymentDriver } } + /** + * + * Authorises a credit card for future use + * @param array $data Array of variables needed for the view + * + * @return view The gateway specific partial to be rendered + * + */ public function authorizeCreditCardView(array $data) { @@ -137,6 +142,11 @@ class StripePaymentDriver extends BasePaymentDriver } + /** + * Processes the gateway response for credti card authorization + * @param Request $request The returning request object + * @return view Returns the user to payment methods screen. + */ public function authorizeCreditCardResponse($request) { @@ -203,7 +213,7 @@ class StripePaymentDriver extends BasePaymentDriver public function processPaymentView(array $data) { $payment_intent_data = [ - 'amount' => $data['amount_with_fee']*100, + 'amount' => $this->convertToStripeAmount($data['amount_with_fee'], $this->client->currency->precision), 'currency' => $this->client->getCurrencyCode(), 'customer' => $this->findOrCreateCustomer(), 'description' => $data['invoices']->pluck('id'), //todo more meaningful description here: @@ -260,18 +270,40 @@ class StripePaymentDriver extends BasePaymentDriver $payment_method = $server_response->payment_method; $payment_status = $server_response->status; $save_card = $request->input('store_card'); - $gateway_type_id = $request->input('gateway_type_id'); - $this->init() + $gateway_type_id = $request->input('payment_method_id'); + $hashed_ids = $request->input('hashed_ids'); + $invoices = Invoice::whereIn('id', $this->transformKeys(explode(",",$hashed_ids))) + ->whereClientId($this->client->id) + ->get(); + /** + * Potential statuses that can be returned + * + * requires_action + * processing + * canceled + * requires_action + * requires_confirmation + * requires_payment_method + * + */ + + if($this->getContact()){ + $client_contact = $this->getContact(); + } + else{ + $client_contact = $invoices->first()->invitations->first()->contact; + } + + $this->init(); $payment_intent = \Stripe\PaymentIntent::retrieve($server_response->id); $customer = $payment_intent->customer; - if($save_card) + if($payment_status == 'succeeded') { - $this->init() + $this->init(); $stripe_payment_method = \Stripe\PaymentMethod::retrieve($payment_method); $stripe_payment_method_obj = $stripe_payment_method->jsonSerialize(); - $stripe_payment_method->attach(['customer' => $customer]); $payment_meta = new \stdClass; @@ -281,8 +313,15 @@ class StripePaymentDriver extends BasePaymentDriver $payment_meta->brand = $stripe_payment_method_obj['card']['brand']; $payment_meta->last4 = $stripe_payment_method_obj['card']['last4']; $payment_meta->type = $stripe_payment_method_obj['type']; + + $payment_type = PaymentType::parseCardType($stripe_payment_method_obj['card']['brand']); } + if($save_card == 'true') + { + + $stripe_payment_method->attach(['customer' => $customer]); + $cgt = new ClientGatewayToken; $cgt->company_id = $this->client->company->id; $cgt->client_id = $this->client->id; @@ -293,16 +332,46 @@ class StripePaymentDriver extends BasePaymentDriver $cgt->meta = $payment_meta; $cgt->save(); - if($is_default == 'true' || $this->client->gateway_tokens->count() == 1) + if($this->client->gateway_tokens->count() == 1) { $this->client->gateway_tokens()->update(['is_default'=>0]); $cgt->is_default = 1; $cgt->save(); } + } + + //todo need to fix this to support payment types other than credit card.... sepa etc etc + if(!$payment_type) + $payment_type = PaymentType::CREDIT_CARD_OTHER; + + $payment = PaymentFactory::create($this->client->company->id, $this->client->user->id); + $payment->client_id = $this->client->id; + $payment->client_contact_id = $client_contact->id; + $payment->company_gateway_id = $this->company_gateway->id; + $payment->status_id = Payment::STATUS_COMPLETED; + $payment->amount = $this->convertFromStripeAmount($server_response->amount, $this->client->currency->precision); + $payment->payment_date = Carbon::now(); + $payment->payment_type_id = $payment_type; + $payment->transaction_reference = $payment_method; + $payment->save(); + + $payment->invoices()->sync($invoices); + $payment->save(); + } + } + private function convertFromStripeAmount($amount, $precision) + { + return $amount / pow(10, $precision); + } + + private function convertToStripeAmount($amount, $precision) + { + return $amount * pow(10, $precision); + } /** * Creates a new String Payment Intent * @@ -377,7 +446,7 @@ class StripePaymentDriver extends BasePaymentDriver } if(!$customer) - throw Exception('Unable to create gateway customer'); + throw new Exception('Unable to create gateway customer'); return $customer; } diff --git a/resources/views/portal/default/gateways/stripe/add_credit_card.blade.php b/resources/views/portal/default/gateways/stripe/add_credit_card.blade.php index dbb8d26c3442..1614d4e61587 100644 --- a/resources/views/portal/default/gateways/stripe/add_credit_card.blade.php +++ b/resources/views/portal/default/gateways/stripe/add_credit_card.blade.php @@ -76,8 +76,7 @@ $("#card-button").removeAttr("disabled"); } else { - // The setup has succeeded. Display a success message. - console.log(result); + The card has been successfullly stored. postResult(result); } }); diff --git a/resources/views/portal/default/gateways/stripe/credit_card.blade.php b/resources/views/portal/default/gateways/stripe/credit_card.blade.php index af14b691766c..97344bff21c6 100644 --- a/resources/views/portal/default/gateways/stripe/credit_card.blade.php +++ b/resources/views/portal/default/gateways/stripe/credit_card.blade.php @@ -80,6 +80,7 @@ $("#card-errors").empty(); $("#card-errors").append("" + result.error.message + ""); $("#card-button").removeAttr("disabled"); + $("#store_card").val(0); } else { // The setup has succeeded. Display a success message.