feat: enable flutterwave payment
This commit is contained in:
parent
5bdc61414f
commit
4de520e861
|
@ -47,3 +47,5 @@ STRIPE_KEY=pk_test_51NAILHJ6IfmAiBwqd8t8ZL9WjTdcMOSDt46TfLT1DS1VPRTrEY0UC3RDUF0b
|
||||||
STRIPE_SECRET=sk_test_51NAILHJ6IfmAiBwqgblKnBatWzIt3mtMYyw9Tc2RRFWUJJDVJ2VGKCBo3o0eTPCAigLB8lAbPiDiuvQ9Arwg0fad00fv7zIJdY
|
STRIPE_SECRET=sk_test_51NAILHJ6IfmAiBwqgblKnBatWzIt3mtMYyw9Tc2RRFWUJJDVJ2VGKCBo3o0eTPCAigLB8lAbPiDiuvQ9Arwg0fad00fv7zIJdY
|
||||||
STRIPE_WEBHOOK_SECRET=your-stripe-webhook-secret
|
STRIPE_WEBHOOK_SECRET=your-stripe-webhook-secret
|
||||||
STRIPE_ACCOUNT=acct_1NAILHJ6IfmAiBwq
|
STRIPE_ACCOUNT=acct_1NAILHJ6IfmAiBwq
|
||||||
|
|
||||||
|
FLW_SECRET_KEY=FLWSECK_TEST-8bf06ec7ac19eb8f045e9f3ebe9dfb97-X
|
||||||
|
|
|
@ -111,11 +111,10 @@ class Handler extends ExceptionHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($exception instanceof ClientException) {
|
if ($exception instanceof ClientException) {
|
||||||
$message = $exception->getResponse()->getBody()->getContents();
|
$response = json_decode($exception->getResponse()->getBody()->getContents());
|
||||||
$code = $exception->getCode();
|
$code = $exception->getCode();
|
||||||
|
|
||||||
Log::error(json_encode($message));
|
return $this->errorResponse($response->message ?? $response , $code);
|
||||||
return $this->errorResponse($message, $code);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if($exception instanceof ConnectException){
|
if($exception instanceof ConnectException){
|
||||||
|
|
|
@ -0,0 +1,194 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Enums\PaymentMethod;
|
||||||
|
use App\Enums\PaymentTransactionStatus;
|
||||||
|
use App\Enums\PaymentType;
|
||||||
|
use App\Models\Country;
|
||||||
|
use App\Models\PaymentAggregator;
|
||||||
|
use App\Models\PaymentTransaction;
|
||||||
|
use DateTime;
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Lang;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Propaganistas\LaravelPhone\PhoneNumber;
|
||||||
|
use Propaganistas\LaravelPhone\Rules\Phone;
|
||||||
|
use Symfony\Component\HttpFoundation\Response as ResponseAlias;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
class FlutterwaveController extends Controller
|
||||||
|
{
|
||||||
|
private $timeout = 60; //In seconds
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new controller instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
public function pay(Request $request)
|
||||||
|
{
|
||||||
|
$this->validate($request, [
|
||||||
|
// 'aggregator_id' => 'required|integer',
|
||||||
|
'amount' => 'required|numeric|min:5',
|
||||||
|
'currency' => 'required|string|size:3',
|
||||||
|
'customer_id' => 'required|integer',
|
||||||
|
'customer_email' => 'required|email',
|
||||||
|
'customer_name' => 'nullable|string',
|
||||||
|
'customer_surname' => 'required|string',
|
||||||
|
'customer_phone_number' => 'required|string',
|
||||||
|
'customer_address' => 'required|string',
|
||||||
|
'reason' => 'required|string'
|
||||||
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
$aggregator = PaymentAggregator::where('name','like','%flutterwave%')->firstOrFail();
|
||||||
|
|
||||||
|
$transaction_id = $this->getTransactionID();
|
||||||
|
$amount = $request->input('amount');
|
||||||
|
$currency = $request->input('currency');
|
||||||
|
|
||||||
|
if ($currency != 'USD') {
|
||||||
|
// Convertir en multiple de 5
|
||||||
|
$amount = $this->roundUpToAny($amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init payment
|
||||||
|
$createResponse = (new Client())->post('https://api.flutterwave.com/v3/payments', [
|
||||||
|
'headers' => [
|
||||||
|
"Authorization" => config('variables.flw_secret_key')
|
||||||
|
],
|
||||||
|
'json' => [
|
||||||
|
"tx_ref" => $transaction_id,
|
||||||
|
"amount" => $amount,
|
||||||
|
"currency" => $request->input('currency'),
|
||||||
|
"redirect_url" => route('flutterwave.webhook'),
|
||||||
|
"customer" => [
|
||||||
|
"email" => $request->input('customer_email'),
|
||||||
|
"phonenumber" => $request->input('customer_phone_number'),
|
||||||
|
"name" => $request->input('customer_surname').' '.$request->input('customer_name')
|
||||||
|
],
|
||||||
|
"customizations" => [
|
||||||
|
"title" => $request->input('reason'),
|
||||||
|
"logo" => 'https://ilink-app.com/backoffice/images/logo_blueback.png'
|
||||||
|
],
|
||||||
|
"meta" => [
|
||||||
|
"customer_id" => $request->input('customer_id'),
|
||||||
|
"customer_address" => $request->input('customer_address')
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'timeout' => $this->timeout
|
||||||
|
]);
|
||||||
|
|
||||||
|
$responseData = json_decode($createResponse->getBody()->getContents());
|
||||||
|
$responseCode = $createResponse->getStatusCode();
|
||||||
|
|
||||||
|
if ($responseCode == 200) {
|
||||||
|
|
||||||
|
PaymentTransaction::create([
|
||||||
|
'aggregator_id' => $aggregator->id,
|
||||||
|
"currency" => $request->input('currency'),
|
||||||
|
"transaction_id" => $transaction_id,
|
||||||
|
"amount" => $amount,
|
||||||
|
"payment_method" => "ALL",
|
||||||
|
"payment_url" => $responseData->data->link,
|
||||||
|
'status' => PaymentTransactionStatus::PENDING,
|
||||||
|
"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' => $responseData->message,
|
||||||
|
'payment_url' => $responseData->data->link
|
||||||
|
], ResponseAlias::HTTP_MOVED_PERMANENTLY);
|
||||||
|
|
||||||
|
}else{
|
||||||
|
return $this->errorResponse($responseData->error->message ?? trans('errors.unexpected_error'),$responseCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function capturePaymentResult(Request $request)
|
||||||
|
{
|
||||||
|
$this->validate($request, [
|
||||||
|
'transaction_id' => 'nullable|string',
|
||||||
|
'tx_ref' => 'nullable|string|exists:payment_transactions,transaction_id',
|
||||||
|
'status' => 'nullable|string'
|
||||||
|
]);
|
||||||
|
|
||||||
|
Log::info(json_encode($request->all()));
|
||||||
|
if($request->has('tx_ref')){
|
||||||
|
$transaction = PaymentTransaction::where('transaction_id',$request->input('tx_ref'))->firstOrFail();
|
||||||
|
return $this->getPaymentStatus($transaction, $request->input('transaction_id'));
|
||||||
|
}else{
|
||||||
|
return response("OK");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getPaymentStatus(PaymentTransaction $transaction, $flwTransactionId)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
|
||||||
|
// Create a client with a base URI
|
||||||
|
$response = (new Client())->get('https://api.flutterwave.com/v3/transactions/'.$flwTransactionId.'/verify', [
|
||||||
|
'headers' => [
|
||||||
|
"Authorization" => config('variables.flw_secret_key')
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$responseData = json_decode($response->getBody()->getContents());
|
||||||
|
$responseCode = $response->getStatusCode();
|
||||||
|
|
||||||
|
if ($responseCode == 200 && $responseData?->data?->status == 'successful'
|
||||||
|
&& $responseData?->data?->tx_ref == $transaction->transaction_id) {
|
||||||
|
|
||||||
|
$transaction->update([
|
||||||
|
'status' => PaymentTransactionStatus::ACCEPTED,
|
||||||
|
'payment_method_exact' => $responseData?->data?->payment_type ?? null,
|
||||||
|
'aggregator_payment_ref' => $responseData?->data?->flw_ref ?? null,
|
||||||
|
'payment_date' => $responseData?->data?->created_at != null ? new DateTime($responseData?->data?->created_at) : null,
|
||||||
|
]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
Log::info("Get Payment Status Error");
|
||||||
|
Log::info($e->getMessage());
|
||||||
|
$transaction->update([
|
||||||
|
'status' => PaymentTransactionStatus::REFUSED
|
||||||
|
]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if($transaction->status == PaymentTransactionStatus::ACCEPTED){
|
||||||
|
return redirect()->route('paymentResult',[
|
||||||
|
'transaction_id' => $transaction->transaction_id,
|
||||||
|
'token' => $transaction->payment_token,
|
||||||
|
'status' => 1
|
||||||
|
]);
|
||||||
|
}else{
|
||||||
|
return redirect()->route('paymentResult',[
|
||||||
|
'message' => "Payment failed",
|
||||||
|
'status' => 0
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -68,6 +68,8 @@ class PaymentController extends Controller
|
||||||
return app(YoomeeV2Controller::class)->initPay($request);
|
return app(YoomeeV2Controller::class)->initPay($request);
|
||||||
case 'cinetpay':
|
case 'cinetpay':
|
||||||
return app(CinetpayController::class)->pay($request);
|
return app(CinetpayController::class)->pay($request);
|
||||||
|
case 'flutterwave':
|
||||||
|
return app(FlutterwaveController::class)->pay($request);
|
||||||
case 'stripe':
|
case 'stripe':
|
||||||
return app(StripeController::class)->payIn($request);
|
return app(StripeController::class)->payIn($request);
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -223,7 +223,7 @@ class StripeController extends Controller
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return $this->errorResponse($errorMessage ?? __('errors.unexpected_error'));
|
return $this->errorResponse(__('errors.unexpected_error'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -459,7 +459,7 @@ class StripeController extends Controller
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->errorResponse($errorMessage ?? __('errors.unexpected_error'));
|
return $this->errorResponse(__('errors.unexpected_error'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -8,4 +8,8 @@ class PaymentTransaction extends Model
|
||||||
{
|
{
|
||||||
protected $table = 'payment_transactions';
|
protected $table = 'payment_transactions';
|
||||||
protected $guarded = ['id'];
|
protected $guarded = ['id'];
|
||||||
|
|
||||||
|
protected $dates = [
|
||||||
|
'payment_date'
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,4 +22,5 @@ return [
|
||||||
'stripe_secret' => env('STRIPE_SECRET', ''),
|
'stripe_secret' => env('STRIPE_SECRET', ''),
|
||||||
'stripe_webhook_secret' => env('STRIPE_WEBHOOK_SECRET', ''),
|
'stripe_webhook_secret' => env('STRIPE_WEBHOOK_SECRET', ''),
|
||||||
'stripe_account' => env('STRIPE_ACCOUNT', ''),
|
'stripe_account' => env('STRIPE_ACCOUNT', ''),
|
||||||
|
'flw_secret_key' => env('FLW_SECRET_KEY', '')
|
||||||
];
|
];
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('payment_transactions', function (Blueprint $table) {
|
||||||
|
$table->dateTime('payment_date')->nullable()->change();
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::table('payment_transactions', function (Blueprint $table) {
|
||||||
|
$table->string('payment_date')->nullable()->change();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
|
@ -41,6 +41,8 @@ $router->addRoute(['GET','POST'],'/yoomee/v2/webhook', ['as' => 'yoomee.v2.webho
|
||||||
$router->addRoute(['GET','POST'],'/cinetpay/webhook', ['as' => 'cinetpay.webhook' , 'uses' => 'CinetpayController@capturePaymentResult']);
|
$router->addRoute(['GET','POST'],'/cinetpay/webhook', ['as' => 'cinetpay.webhook' , 'uses' => 'CinetpayController@capturePaymentResult']);
|
||||||
$router->addRoute(['GET','POST'],'/cinetpay/transfert/webhook', ['as' => 'cinetpay.transfert.webhook' , 'uses' => 'CinetpayController@captureTransfertResult']);
|
$router->addRoute(['GET','POST'],'/cinetpay/transfert/webhook', ['as' => 'cinetpay.transfert.webhook' , 'uses' => 'CinetpayController@captureTransfertResult']);
|
||||||
$router->addRoute(['GET','POST'],'/stripe/webhook', ['as' => 'stripe.webhook' , 'uses' => 'StripeController@capturePaymentResult']);
|
$router->addRoute(['GET','POST'],'/stripe/webhook', ['as' => 'stripe.webhook' , 'uses' => 'StripeController@capturePaymentResult']);
|
||||||
|
$router->addRoute(['GET','POST'],'/flutterwave/webhook', ['as' => 'flutterwave.webhook' , 'uses' => 'FlutterwaveController@capturePaymentResult']);
|
||||||
|
|
||||||
|
|
||||||
$router->addRoute(['GET','POST'],'/paymentResult', ['as' => 'paymentResult' , 'uses' => 'PaymentController@paymentResult']);
|
$router->addRoute(['GET','POST'],'/paymentResult', ['as' => 'paymentResult' , 'uses' => 'PaymentController@paymentResult']);
|
||||||
|
|
||||||
|
@ -79,6 +81,15 @@ $router->group(['middleware' => 'auth'], function () use ($router) {
|
||||||
$router->get('checkBalance',['as' => 'cinetpay.check-balance', 'uses' => 'CinetpayController@checkBalance']);
|
$router->get('checkBalance',['as' => 'cinetpay.check-balance', 'uses' => 'CinetpayController@checkBalance']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flutterwave Endpoints
|
||||||
|
*/
|
||||||
|
$router->group(['prefix' => 'flutterwave'], function () use ($router) {
|
||||||
|
$router->addRoute(['GET','POST'],'pay',['as' => 'flutterwave.pay', 'uses' => 'FlutterwaveController@pay']);
|
||||||
|
$router->post('payOut',['as' => 'flutterwave.payout', 'uses' => 'FlutterwaveController@payOut']);
|
||||||
|
$router->get('checkBalance',['as' => 'flutterwave.check-balance', 'uses' => 'FlutterwaveController@checkBalance']);
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stripe Endpoints
|
* Stripe Endpoints
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue