2023-05-30 01:37:16 +00:00
< ? php
namespace App\Http\Controllers ;
2023-06-07 19:56:04 +00:00
use App\Enums\PaymentTransactionStatus ;
use App\Models\PaymentAggregator ;
use App\Models\PaymentRefund ;
2023-05-30 01:37:16 +00:00
use App\Models\PaymentTransaction ;
use Illuminate\Http\Request ;
use Illuminate\Http\Response ;
use Illuminate\Support\Facades\DB ;
use Illuminate\Support\Facades\Log ;
use Illuminate\Support\Str ;
2023-06-07 19:56:04 +00:00
use Stripe\PaymentIntent ;
2023-05-30 01:37:16 +00:00
use Stripe\StripeClient ;
use Throwable ;
class StripeController extends Controller
{
private $client ;
private $timeout = 60 ; //In seconds
/**
* Create a new controller instance .
*
* @ return void
*/
public function __construct ()
{
// Create a client with a base URI
$this -> client = new StripeClient ( config ( 'variables.stripe_secret' ));
}
public function getMethods ()
{
$data = $this -> client -> paymentMethods -> all () -> toArray ();
return $this -> successResponse ([
'hasWebview' => true ,
'methods' => $data ,
]);
}
public function getCheckout ( $payment_token )
{
$transaction = PaymentTransaction :: where ( 'payment_token' , $payment_token ) -> first ();
if ( empty ( $transaction )){
return $this -> errorResponse ( __ ( 'errors.model_not_found' , [ 'model' => 'transaction' ]), Response :: HTTP_NOT_FOUND );
}
$amount = $transaction -> amount ;
$receiver = config ( 'variables.receiver_name' );
$receiver_logo = asset ( 'assets/images/logo.jpeg' );
return view ( 'stripe-checkout' , compact ( 'amount' , 'receiver' , 'receiver_logo' , 'payment_token' ));
}
public function pay ( Request $request )
{
try {
$this -> validate ( $request , [
'aggregator_id' => 'required|integer' ,
'amount' => 'required|numeric|min:5' ,
'currency' => 'required|string|size:3' ,
2023-06-07 19:56:04 +00:00
'customer_id' => 'nullable' ,
2023-05-30 01:37:16 +00:00
'payment_method' => 'nullable|string' ,
'customer_email' => 'required|email' ,
2023-06-07 19:56:04 +00:00
'customer_name' => 'nullable|string' ,
2023-05-30 01:37:16 +00:00
'customer_surname' => 'required|string' ,
2023-06-07 19:56:04 +00:00
'customer_phone_number' => 'nullable|string' ,
2023-05-30 01:37:16 +00:00
'customer_address' => 'required|string' ,
2023-06-07 19:56:04 +00:00
'customer_city' => 'required_if:payment_method,CARD|string' ,
2023-05-30 01:37:16 +00:00
'customer_country' => 'required|string|size:2' ,
2023-06-07 19:56:04 +00:00
'customer_state' => 'required_if:payment_method,CARD|string|size:2' , //Etat du pays dans lequel se trouve le client. Cette valeur est obligatoire si le client se trouve au États Unis d’ Amérique (US) ou au Canada (CA)
'customer_zip_code' => 'required_if:payment_method,CARD|string|size:5' ,
2023-05-30 01:37:16 +00:00
'reason' => 'required|string'
]);
$transaction_id = $this -> getTransactionID ();
2023-06-07 19:56:04 +00:00
$payment_method = $request -> input ( 'payment_method' , 'CARD' );
2023-05-30 01:37:16 +00:00
$amount = $request -> input ( 'amount' );
$currency = $request -> input ( 'currency' );
if ( $currency != 'USD' ) {
// Convertir en multiple de 5
$amount = $this -> roundUpToAny ( $amount );
}
if ( $amount < 325 && $currency == 'XAF' ){
$amount = 325 ;
}
$payment_token = $this -> getTransactionToken ();
$payment_url = route ( 'stripe.checkout' ,[ 'payment_token' => $payment_token ]);
PaymentTransaction :: create ([
'aggregator_id' => $request -> input ( 'aggregator_id' ),
" currency " => $request -> input ( 'currency' ),
" transaction_id " => $transaction_id ,
" amount " => $amount ,
" payment_method " => $payment_method ,
" payment_token " => $payment_token ,
" payment_url " => $payment_url ,
2023-06-07 19:56:04 +00:00
'status' => PaymentTransactionStatus :: INITIATED ,
2023-05-30 01:37:16 +00:00
" reason " => $request -> input ( 'reason' ),
" customer_id " => $request -> input ( 'customer_id' ),
" customer_name " => $request -> input ( 'customer_name' ),
" customer_surname " => $request -> input ( 'customer_surname' ),
" customer_email " => $request -> input ( 'customer_email' ),
" customer_phone_number " => $request -> input ( 'customer_phone_number' ),
" customer_address " => $request -> input ( 'customer_address' ),
" customer_city " => $request -> input ( 'customer_city' ),
" customer_country " => $request -> input ( 'customer_country' ),
" customer_state " => $request -> input ( 'customer_state' ),
" customer_zip_code " => $request -> input ( 'customer_zip_code' ),
]);
return $this -> successResponse ([
'message' => 'Payment initiated' ,
'payment_url' => $payment_url
]);
} catch ( Throwable $e ){
Log :: error ( $e -> getMessage ());
}
return $this -> errorResponse ( trans ( 'errors.unexpected_error' ));
}
public function post ( Request $request )
{
$this -> validate ( $request , [
'payment_token' => 'required|string|exists:payment_transactions,payment_token' ,
'stripeToken' => 'required|string' ,
]);
$token = $request -> input ( 'payment_token' );
2023-06-07 19:56:04 +00:00
$transaction = PaymentTransaction :: where ( 'payment_token' , $token ) -> where ( 'status' , PaymentTransactionStatus :: INITIATED ) -> firstOrFail ();
2023-05-30 01:37:16 +00:00
// Init payment
$charge = $this -> client -> charges -> create ([
2023-06-08 10:24:56 +00:00
" amount " => stripeFormatCurrency ( round ( $transaction -> amount , 2 ), $transaction -> currency ),
2023-05-30 01:37:16 +00:00
" currency " => $transaction -> currency ,
" description " => $transaction -> reason ,
// "customer" => 15,
" source " => $request -> input ( 'stripeToken' ),
" receipt_email " => $transaction -> customer_email ,
" metadata " => [
" transaction_id " => $transaction -> transaction_id ,
'lang' => app () -> getLocale (),
" customer_id " => $transaction -> customer_id ,
" customer_name " => $transaction -> customer_name ,
" customer_surname " => $transaction -> customer_surname ,
" customer_email " => $transaction -> customer_email ,
" customer_phone_number " => $transaction -> customer_phone_number ,
" customer_address " => $transaction -> customer_address ,
" customer_city " => $transaction -> customer_city ,
" customer_country " => $transaction -> customer_country ,
" customer_state " => $transaction -> customer_state ,
" customer_zip_code " => $transaction -> customer_zip_code ,
],
]);
if ( $charge ){
$transaction -> update ([
'aggregator_payment_ref' => $charge -> id ,
2023-06-07 19:56:04 +00:00
'payment_date' => date ( 'Y-m-d H:i:s' , $charge -> created ),
'status' => PaymentTransactionStatus :: ACCEPTED ,
2023-05-30 01:37:16 +00:00
'payment_method_exact' => $request -> input ( 'card_number' )
]);
return redirect () -> route ( 'checkout' ,[ 'payment_token' => $token ]);
}
session () -> flash ( 'error' , __ ( 'errors.unexpected_error' ));
return redirect () -> to ( route ( 'stripe.checkout' ,[ 'payment_token' => $token ]));
}
private function getTransactionToken ()
{
do {
$code = 'STP-' . Str :: random ( 64 );
$result = collect ( DB :: select ( 'SELECT * FROM payment_transactions WHERE payment_token = :code' , [ 'code' => $code ]));
$codeCorrect = sizeof ( $result ) < 0 ;
} while ( $codeCorrect );
return $code ;
}
2023-06-07 19:56:04 +00:00
2023-06-08 10:24:56 +00:00
2023-06-07 19:56:04 +00:00
public function refund ( Request $request )
{
$this -> validate ( $request , [
'transaction_id' => 'required|string|exists:payment_transactions,transaction_id' ,
'reason' => 'nullable|string|in:duplicate,fraudulent,requested_by_customer'
]);
$transaction = PaymentTransaction :: where ( 'transaction_id' , $request -> input ( 'transaction_id' )) -> where ( 'status' , PaymentTransactionStatus :: ACCEPTED ) -> firstOrFail ();
$reason = $request -> input ( 'reason' , 'requested_by_customer' );
try {
$reference = $transaction -> aggregator_payment_ref ;
$twoFirstChars = substr ( $reference , 0 , 2 );
$keyToRefund = $twoFirstChars == 'ch' ? 'charge' : 'payment_intent' ;
$refund = $this -> client -> refunds -> create ([
$keyToRefund => $reference ,
'reason' => $reason
]);
if ( ! empty ( $refund )){
PaymentRefund :: create ([
'refund_id' => $this -> getTransactionID ( 'payment_refunds' ),
'transaction_id' => $transaction -> id ,
" currency " => strtoupper ( $refund -> currency ),
" amount " => $refund -> amount ,
" reason " => $reason ,
'date' => date ( 'Y-m-d H:i:s' , $refund -> created ),
'status' => strtoupper ( $refund -> status )
]);
return $this -> successResponse ( " Payment refunded successfully " );
}
} catch ( Throwable $e ){
Log :: error ( " Error Stripe refund " );
$errorMessage = $e -> getMessage ();
Log :: error ( $errorMessage );
}
return $this -> errorResponse ( $errorMessage ? ? __ ( 'errors.unexpected_error' ));
}
public function payIn ( Request $request )
{
$this -> validate ( $request , [
'card_no' => 'required' ,
'exp_month' => 'required' ,
'exp_year' => 'required' ,
'cvc' => 'required' ,
// 'aggregator_id' => 'required|integer',
'amount' => 'required|numeric|min:5' ,
'currency' => 'required|string|size:3' ,
'customer_id' => 'nullable' ,
// 'payment_method' => 'nullable|string', // Actuallu it's only CARD
'customer_email' => 'required|email' ,
'customer_name' => 'nullable|string' ,
'customer_surname' => 'required|string' ,
'customer_phone_number' => 'nullable|string' ,
'customer_address' => 'required|string' ,
'customer_city' => 'required|string' ,
'customer_country' => 'required|string|size:2' ,
'customer_state' => 'required_if:customer_country,US|string|size:2' , //Etat du pays dans lequel se trouve le client. Cette valeur est obligatoire si le client se trouve au États Unis d’ Amérique (US) ou au Canada (CA)
'customer_zip_code' => 'required_if:customer_country,US|string|size:5' ,
'reason' => 'required|string'
]);
$aggregator = PaymentAggregator :: where ( 'name' , 'like' , '%stripe%' ) -> firstOrFail ();
try {
$amount = $request -> input ( 'amount' );
$currency = $request -> input ( 'currency' );
if ( $currency != 'USD' ) {
// Convertir en multiple de 5
$amount = $this -> roundUpToAny ( $amount );
}
if ( $amount < 325 && $currency == 'XAF' ){
$amount = 325 ;
}
$transaction = PaymentTransaction :: create ([
'aggregator_id' => $aggregator -> id ,
" currency " => $request -> input ( 'currency' ),
" transaction_id " => $this -> getTransactionID (),
" amount " => $amount ,
" payment_method " => 'CARD' ,
'status' => PaymentTransactionStatus :: INITIATED ,
" reason " => $request -> input ( 'reason' ),
" customer_id " => $request -> input ( 'customer_id' ),
" customer_name " => $request -> input ( 'customer_name' ),
" customer_surname " => $request -> input ( 'customer_surname' ),
" customer_email " => $request -> input ( 'customer_email' ),
" customer_phone_number " => $request -> input ( 'customer_phone_number' ),
" customer_address " => $request -> input ( 'customer_address' ),
" customer_city " => $request -> input ( 'customer_city' ),
" customer_country " => $request -> input ( 'customer_country' ),
" customer_state " => $request -> input ( 'customer_state' ),
" customer_zip_code " => $request -> input ( 'customer_zip_code' ),
]);
// Create payment method in Stripe
// https://stripe.com/docs/api/payment_methods/create
$method = $this -> client -> paymentMethods -> create ([
'type' => 'card' ,
'card' => [
'number' => $request -> input ( 'card_no' ),
'exp_month' => $request -> input ( 'exp_month' ),
'exp_year' => $request -> input ( 'exp_year' ),
'cvc' => $request -> input ( 'cvc' ),
]
]);
if ( $method ){
$transaction -> update ([
'payment_method_exact' => $method -> id
]);
// Create payment intent
// https://stripe.com/docs/api/payment_intents/create
$paymentIntent = $this -> client -> paymentIntents -> create ([
2023-06-08 10:24:56 +00:00
" amount " => stripeFormatCurrency ( $amount , $request -> input ( 'currency' )),
2023-06-07 19:56:04 +00:00
" currency " => $request -> input ( 'currency' ),
" description " => $request -> input ( 'reason' ),
'payment_method_types' => [ 'card' ],
'payment_method' => $method -> id ,
'confirm' => 'true' ,
'capture_method' => 'automatic' ,
'return_url' => route ( 'stripe.webhook' ,[ 'transaction_id' => $transaction -> transaction_id ]), // Redirect url if 3d secure required
'payment_method_options' => [
'card' => [
'request_three_d_secure' => 'automatic'
]
],
]);
if ( $paymentIntent ){
$transaction -> update ([
'payment_date' => date ( 'Y-m-d H:i:s' , $paymentIntent -> created ),
'aggregator_payment_ref' => $paymentIntent -> id
]);
if ( ! empty ( $paymentIntent -> next_action -> redirect_to_url -> url )){
return $this -> successResponse ([
'message' => '3D secure redirect' ,
'payment_url' => $paymentIntent -> next_action -> redirect_to_url -> url
], Response :: HTTP_MOVED_PERMANENTLY );
}
return $this -> handlePaymentIntentResult ( $transaction , $paymentIntent );
}
}
} catch ( Throwable $e ){
Log :: error ( " Error Stripe submit payment intent " );
$errorMessage = $e -> getMessage ();
Log :: error ( $errorMessage );
}
return $this -> errorResponse ( $errorMessage ? ? __ ( 'errors.unexpected_error' ));
}
public function payOut ( Request $request )
{
$this -> validate ( $request , [
'card_no' => 'required_if:payment_method,CARD' ,
'exp_month' => 'required_if:payment_method,CARD' ,
'exp_year' => 'required_if:payment_method,CARD' ,
'cvc' => 'required_if:payment_method,CARD' ,
'bank_country' => 'required_if:payment_method,BANK|string|size:2' ,
'bank_currency' => 'required_if:payment_method,BANK|string|size:3' ,
'bank_account_number' => 'required_if:payment_method,BANK|string' ,
// 'aggregator_id' => 'required|integer',
'amount' => 'required|numeric|min:5' ,
'currency' => 'required|string|size:3' ,
'customer_id' => 'nullable' ,
'payment_method' => 'required|string|in:CARD,BANK' ,
'customer_email' => 'required|email' ,
'customer_name' => 'nullable|string' ,
'customer_surname' => 'required|string' ,
'customer_phone_number' => 'nullable|string' ,
'customer_address' => 'required|string' ,
'customer_city' => 'required|string' ,
'customer_country' => 'required|string|size:2' ,
'customer_state' => 'required_if:customer_country,US|string|size:2' , //Etat du pays dans lequel se trouve le client. Cette valeur est obligatoire si le client se trouve au États Unis d’ Amérique (US) ou au Canada (CA)
'customer_zip_code' => 'required_if:customer_country,US|string|size:5' ,
'reason' => 'required|string'
]);
$aggregator = PaymentAggregator :: where ( 'name' , 'like' , '%stripe%' ) -> firstOrFail ();
try {
$amount = $request -> input ( 'amount' );
$currency = $request -> input ( 'currency' );
$payment_method = $request -> input ( 'payment_method' );
$transaction = PaymentTransaction :: create ([
'aggregator_id' => $aggregator -> id ,
" currency " => $request -> input ( 'currency' ),
" transaction_id " => $this -> getTransactionID (),
" amount " => $amount ,
" payment_method " => $payment_method ,
'status' => PaymentTransactionStatus :: INITIATED ,
" reason " => $request -> input ( 'reason' ),
" customer_id " => $request -> input ( 'customer_id' ),
" customer_name " => $request -> input ( 'customer_name' ),
" customer_surname " => $request -> input ( 'customer_surname' ),
" customer_email " => $request -> input ( 'customer_email' ),
" customer_phone_number " => $request -> input ( 'customer_phone_number' ),
" customer_address " => $request -> input ( 'customer_address' ),
" customer_city " => $request -> input ( 'customer_city' ),
" customer_country " => $request -> input ( 'customer_country' ),
" customer_state " => $request -> input ( 'customer_state' ),
" customer_zip_code " => $request -> input ( 'customer_zip_code' ),
]);
// Create destination account in Stripe
if ( $payment_method == 'CARD' ){
//https://stripe.com/docs/api/external_account_cards/create
$destination = $this -> client -> accounts -> createExternalAccount (
config ( 'variables.stripe_account' ), [
'external_account' => [
'object' => 'card' ,
'number' => $request -> input ( 'card_no' ),
'exp_month' => $request -> input ( 'exp_month' ),
'exp_year' => $request -> input ( 'exp_year' ),
'cvc' => $request -> input ( 'cvc' ),
]
]
);
} else {
//https://stripe.com/docs/api/external_account_bank_accounts/create#account_create_bank_account
$destination = $this -> client -> accounts -> createExternalAccount (
config ( 'variables.stripe_account' ), [
'external_account' => [
'object' => 'bank_account' ,
'country' => $request -> input ( 'bank_country' ),
'currency' => $request -> input ( 'bank_currency' ),
'account_number' => $request -> input ( 'bank_account_number' ),
]
]
);
}
if ( $destination ){
$transaction -> update ([
'payment_method_exact' => $destination -> id
]);
// Create payout in Stripe
// https://stripe.com/docs/api/payouts/create
$payout = $this -> client -> payouts -> create ([
2023-06-08 10:24:56 +00:00
" amount " => stripeFormatCurrency ( $amount , $currency ),
2023-06-07 19:56:04 +00:00
" currency " => $currency ,
'description' => $request -> input ( 'reason' ),
" destination " => $destination -> id ,
" method " => $payment_method == 'CARD' ? 'instant' : 'standard'
]);
if ( $payout ) {
$transaction -> update ([
'payment_date' => date ( 'Y-m-d H:i:s' , $payout -> arrival_date ),
'aggregator_payment_ref' => $payout -> id ,
'status' => strtolower ( $payout -> status )
]);
return $this -> successResponse ([
'transaction_id' => $transaction -> transaction_id ,
'status' => $transaction -> status
]);
}
}
} catch ( Throwable $e ){
Log :: error ( " Error Stripe submit payment intent " );
$errorMessage = $e -> getMessage ();
Log :: error ( $errorMessage );
}
return $this -> errorResponse ( $errorMessage ? ? __ ( 'errors.unexpected_error' ));
}
public function capturePaymentResult ( Request $request )
{
$this -> validate ( $request , [
'payment_intent' => 'nullable|string' ,
'transaction_id' => 'required|string|exists:payment_transactions,transaction_id' ,
]);
$transaction = PaymentTransaction :: where ( 'transaction_id' , $request -> input ( 'transaction_id' )) -> firstOrFail ();
if ( $request -> has ( 'payment_intent' )) {
$paymentIntent = $this -> client -> paymentIntents -> retrieve ( $request -> input ( 'payment_intent' ));
return $this -> handlePaymentIntentResult ( $transaction , $paymentIntent );
}
return $this -> errorResponse ( __ ( 'errors.unexpected_error' ));
}
private function handlePaymentIntentResult ( PaymentTransaction $transaction , PaymentIntent $intent ){
if ( $intent -> status == 'succeeded' ){
$transaction -> update ([
'status' => PaymentTransactionStatus :: ACCEPTED
]);
return $this -> successResponse ([
'message' => 'Payment Accepted' ,
'transaction_id' => $transaction -> transaction_id ,
'status' => $transaction -> status
]);
} else {
$transaction -> update ([
'status' => strtolower ( $intent -> status )
]);
return $this -> errorResponse ([
'message' => 'Payment ' . $intent -> status ,
'status' => $transaction -> status
]);
}
}
2023-05-30 01:37:16 +00:00
}