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_WEBHOOK_SECRET=your-stripe-webhook-secret
|
||||
STRIPE_ACCOUNT=acct_1NAILHJ6IfmAiBwq
|
||||
|
||||
FLW_SECRET_KEY=FLWSECK_TEST-8bf06ec7ac19eb8f045e9f3ebe9dfb97-X
|
||||
|
|
|
@ -111,11 +111,10 @@ class Handler extends ExceptionHandler
|
|||
}
|
||||
|
||||
if ($exception instanceof ClientException) {
|
||||
$message = $exception->getResponse()->getBody()->getContents();
|
||||
$response = json_decode($exception->getResponse()->getBody()->getContents());
|
||||
$code = $exception->getCode();
|
||||
|
||||
Log::error(json_encode($message));
|
||||
return $this->errorResponse($message, $code);
|
||||
return $this->errorResponse($response->message ?? $response , $code);
|
||||
}
|
||||
|
||||
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);
|
||||
case 'cinetpay':
|
||||
return app(CinetpayController::class)->pay($request);
|
||||
case 'flutterwave':
|
||||
return app(FlutterwaveController::class)->pay($request);
|
||||
case 'stripe':
|
||||
return app(StripeController::class)->payIn($request);
|
||||
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 $guarded = ['id'];
|
||||
|
||||
protected $dates = [
|
||||
'payment_date'
|
||||
];
|
||||
}
|
||||
|
|
|
@ -22,4 +22,5 @@ return [
|
|||
'stripe_secret' => env('STRIPE_SECRET', ''),
|
||||
'stripe_webhook_secret' => env('STRIPE_WEBHOOK_SECRET', ''),
|
||||
'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/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'],'/flutterwave/webhook', ['as' => 'flutterwave.webhook' , 'uses' => 'FlutterwaveController@capturePaymentResult']);
|
||||
|
||||
|
||||
$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']);
|
||||
});
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue