From d0e69790b3746865850aaa333e0f430e045d8539 Mon Sep 17 00:00:00 2001 From: Djery-Tom Date: Sat, 8 Oct 2022 22:12:51 +0100 Subject: [PATCH] Add yoomee v2 controller --- .env.example | 4 + app/Enums/PaymentTransactionState.php | 1 + app/Http/Controllers/CinetpayController.php | 27 ++- app/Http/Controllers/PaymentController.php | 23 +++ app/Http/Controllers/YoomeeController.php | 71 +++---- app/Http/Controllers/YoomeeV2Controller.php | 209 ++++++++++++++++++++ config/variables.php | 4 + routes/web.php | 12 +- 8 files changed, 299 insertions(+), 52 deletions(-) create mode 100644 app/Http/Controllers/YoomeeV2Controller.php diff --git a/.env.example b/.env.example index 5bd4e01..2c0b087 100644 --- a/.env.example +++ b/.env.example @@ -25,6 +25,10 @@ SWAGGER_DOCS_TOKEN=kTKyDQJhYZLkm7wVZx2pJ90CuYpHcndx YOOMEE_MERCHANT_CODE=47ttazydghzd/bfzdv85z4dzhutFRYRyzhdgtrehdnfglltmfjkk85421@er YOOMEE_API_URL=https://quality-env.yoomeemoney.cm/yoomee_ext_paymentv3/ +YOOMEE_API_V2_URL=https://quality-env.yoomeemoney.cm/run/b2i/collect/webservice/payment/ +YOOMEE_APP_ID=iLinkWorld +YOOMEE_USERNAME=ilink2022 +YOOMEE_PASSWORD=Ilink@2022 CINETPAY_API_KEY=176445314662bebd39b1b6f8.42908045 CINETPAY_SECRET_KEY=140983310662e79736e8ae45.38146680 diff --git a/app/Enums/PaymentTransactionState.php b/app/Enums/PaymentTransactionState.php index 29cb247..d19691b 100644 --- a/app/Enums/PaymentTransactionState.php +++ b/app/Enums/PaymentTransactionState.php @@ -10,4 +10,5 @@ abstract class PaymentTransactionState const PENDING = 'PENDING'; const PENDING_OTP = 'PENDING_OTP'; const REFUSED = 'REFUSED'; + const CANCELLED = 'CANCELLED'; } diff --git a/app/Http/Controllers/CinetpayController.php b/app/Http/Controllers/CinetpayController.php index 96067e8..ce1864d 100644 --- a/app/Http/Controllers/CinetpayController.php +++ b/app/Http/Controllers/CinetpayController.php @@ -27,6 +27,31 @@ class CinetpayController extends Controller ]); } + /** + * @OA\Get( + * path="/cinetpay/methods", + * summary="Afficher la liste des methodes de Cinetpay", + * tags={"Cinetpay"}, + * security={{"api_key":{}}}, + * @OA\Response( + * response=200, + * description="OK", + * @OA\JsonContent( + * ref="#/components/schemas/ApiResponse", + * example = { + * "status" : 200, + * "response" : {{"ALL","MOBILE_MONEY","CREDIT_CARD","WALLET"}}, + * "error":null + * } + * ) + * ) + * ) + */ + public function getMethods() + { + return $this->successResponse(['ALL', 'MOBILE_MONEY','CREDIT_CARD','WALLET']); + } + // public function pay(Request $request) { @@ -130,8 +155,6 @@ class CinetpayController extends Controller 'cpm_trans_id' => 'nullable|string|exists:payment_transactions,transaction_id' ]); - Log::info(json_encode($request->all())); - if($request->has('cpm_trans_id')) { $data = $request->input('cpm_site_id') . $request->input('cpm_trans_id') . $request->input('cpm_trans_date') . $request->input('cpm_amount') . $request->input('cpm_currency') . diff --git a/app/Http/Controllers/PaymentController.php b/app/Http/Controllers/PaymentController.php index 3e3bd38..dfd269a 100644 --- a/app/Http/Controllers/PaymentController.php +++ b/app/Http/Controllers/PaymentController.php @@ -4,9 +4,30 @@ namespace App\Http\Controllers; use App\Models\PaymentAggregator; use Illuminate\Http\Request; +use Illuminate\Support\Facades\Log; class PaymentController extends Controller { + public function getMethods(Request $request) + { + $this->validate($request, [ + 'aggregator_id' => 'required|integer|exists:payment_aggregators,id', + ]); + + $aggregator = PaymentAggregator::findOrFail($request->input('aggregator_id')); + + switch(strtolower($aggregator->name)){ + case 'yoomee': + return redirect()->route('yoomee.methods', $request->all()); + case 'yoomeev2': + return redirect()->route('yoomee.v2.methods', $request->all()); + case 'cinetpay': + return redirect()->route('cinetpay.methods', $request->all()); + default: + return $this->errorResponse(__('errors.unexpected_error')); + } + } + public function pay(Request $request) { $this->validate($request, [ @@ -18,6 +39,8 @@ class PaymentController extends Controller switch(strtolower($aggregator->name)){ case 'yoomee': return redirect()->route('yoomee.pay', $request->all()); + case 'yoomeev2': + return redirect()->route('yoomee.v2.pay', $request->all()); case 'cinetpay': return redirect()->route('cinetpay.pay', $request->all()); default: diff --git a/app/Http/Controllers/YoomeeController.php b/app/Http/Controllers/YoomeeController.php index 3f851fb..6a80354 100644 --- a/app/Http/Controllers/YoomeeController.php +++ b/app/Http/Controllers/YoomeeController.php @@ -6,8 +6,7 @@ use App\Enums\PaymentTransactionState; use App\Models\PaymentTransaction; use GuzzleHttp\Client; use Illuminate\Http\Request; -use Illuminate\Support\Facades\DB; -use Illuminate\Support\Facades\Log; +use Illuminate\Support\Str; class YoomeeController extends Controller { @@ -29,8 +28,8 @@ class YoomeeController extends Controller /** * @OA\Get( - * path="/yoomee/operators", - * summary="Afficher la liste des operateurs de Yoomee", + * path="/yoomee/methods", + * summary="Afficher la liste des methodes de paiements de Yoomee", * tags={"Yoomee"}, * security={{"api_key":{}}}, * @OA\Response( @@ -47,7 +46,7 @@ class YoomeeController extends Controller * ) * ) */ - public function getOperators() + public function getMethods() { $response = $this->client->get('operators'); return $this->successResponse(json_decode($response->getBody()->getContents())); @@ -77,20 +76,20 @@ class YoomeeController extends Controller $otp = $request->input('otp'); // Create passport payment - $createResponse = $this->client->post('passport',[ + $createResponse = $this->client->post('passport', [ 'json' => [ - "currency" => $request->input('currency'), - "customerEmail" => $request->input('customer_email'), - "customerName" => $request->input('customer_name'), - "customerPhone" => $request->input('customer_phone_number'), - "merchantCode" => config('variables.yoomee_merchant_code'), - "orderNumber" => $transaction_id, - "transactionAmount" => $request->input('amount') + "currency" => $request->input('currency'), + "customerEmail" => $request->input('customer_email'), + "customerName" => $request->input('customer_name'), + "customerPhone" => $request->input('customer_phone_number'), + "merchantCode" => config('variables.yoomee_merchant_code'), + "orderNumber" => $transaction_id, + "transactionAmount" => $request->input('amount') ], - 'timeout'=> $this->timeout + 'timeout' => $this->timeout ]); - if($createResponse->getStatusCode() == 201){ + if ($createResponse->getStatusCode() == 201) { $createResponse = json_decode($createResponse->getBody()->getContents()); $transaction = PaymentTransaction::create([ @@ -99,6 +98,7 @@ class YoomeeController extends Controller "transaction_id" => $transaction_id, "amount" => $createResponse->transactionAmount, "payment_method" => $payment_method, + 'payment_token' => Str::random(32), 'state' => $createResponse->paymentStatus, "reason" => $request->input('reason'), "customer_id" => $request->input('customer_id'), @@ -120,18 +120,18 @@ class YoomeeController extends Controller "merchantCode" => config('variables.yoomee_merchant_code'), "paymentMethod" => $payment_method ]; - if(!empty($otp)){ + if (!empty($otp)) { $paymentData['secretOTP'] = $otp; } - $payResponse = $this->client->put("passport/pay/$transaction_id",[ + $payResponse = $this->client->put("passport/pay/$transaction_id", [ 'json' => $paymentData, - 'timeout'=> $this->timeout + 'timeout' => $this->timeout ]); $responseData = json_decode($payResponse->getBody()->getContents()); $responseCode = $payResponse->getStatusCode(); - if($responseCode == 202){ + if ($responseCode == 202) { $transaction->update([ 'state' => PaymentTransactionState::ACCEPTED, 'aggregator_payment_ref' => $this->getTransactionRef($responseData->message), @@ -140,40 +140,17 @@ class YoomeeController extends Controller 'transaction_id' => $transaction_id, 'token' => $transaction->payment_token ]); - }else{ - return $this->errorResponse($responseData,$responseCode); + } else { + return $this->errorResponse($responseData, $responseCode); } } return $this->errorResponse(__('errors.unexpected_error')); } - private function getTransactionRef($message){ - $needle = 'REF: '; - return substr($message,strlen($needle) + strpos($message,$needle)); - } - - public function capturePaymentResult(Request $request) + private function getTransactionRef($message) { - $this->validate($request, [ - 'action' => 'required|in:return,cancel', - 'cpm_trans_id' => 'required|integer|exists:payment_transactions,transaction_id' - ]); - - $action = $request->input('action'); - $transaction = PaymentTransaction::find($request->input('PaymentTransaction_id')); - if (!isset($transaction)) { - return redirect()->to(route('PaymentTransactions.create'))->with('error', __('messages.not_found_payment')); - } - - if ($action == 'cancel') { - $transaction->update([ - 'state' => PaymentTransactionState::CANCELLED - ]); - return redirect()->to(route('PaymentTransactions.create'))->with('success', __('messages.cancelled_payment')); - } else { - return $this->getPaymentStatus($transaction, route('PaymentTransactions.create')); - } + $needle = 'REF: '; + return substr($message, strlen($needle) + strpos($message, $needle)); } - } diff --git a/app/Http/Controllers/YoomeeV2Controller.php b/app/Http/Controllers/YoomeeV2Controller.php new file mode 100644 index 0000000..4fde2af --- /dev/null +++ b/app/Http/Controllers/YoomeeV2Controller.php @@ -0,0 +1,209 @@ +client = new Client([ + 'base_uri' => config('variables.yoomee_api_v2_url'), + 'timeout' => $this->timeout, + 'http_errors' => false + ]); + } + + /** + * @OA\Get( + * path="/yoomee/v2/methods", + * summary="Afficher la liste des methodes de paiment de Yoomee", + * tags={"Yoomee"}, + * security={{"api_key":{}}}, + * @OA\Response( + * response=200, + * description="OK", + * @OA\JsonContent( + * ref="#/components/schemas/ApiResponse", + * example = { + * "status" : 200, + * "response" : {{"Yoomee","MTN","Orange","EU"}}, + * "error":null + * } + * ) + * ) + * ) + */ + public function getMethods() + { + $response = $this->client->get('providers/v1'); + return $this->successResponse(json_decode($response->getBody()->getContents())); + } + + + public function pay(Request $request) + { + $this->validate($request, [ + 'aggregator_id' => 'required|integer|exists:payment_aggregators,id', + 'amount' => 'required|numeric|min:5', + 'currency' => 'required|string|size:3', + 'payment_method' => 'required|string', + 'customer_id' => 'required|integer', + 'customer_email' => 'required|email', + 'customer_name' => 'required|string', + 'customer_surname' => 'required|string', + 'customer_phone_number' => 'required|string', + 'customer_address' => 'required|string', + 'customer_country' => 'required|string|size:2', + 'reason' => 'required|string', + ]); + + $transaction_id = $this->getTransactionID(); + $payment_method = $request->input('payment_method'); + $customer_phone_number = $request->input('customer_phone_number'); + if(str_contains($customer_phone_number,'+237')){ + $customer_phone_number = substr($customer_phone_number,4); + } + // Create passport payment + $createResponse = $this->client->post('start/v1', [ + 'json' => [ + 'order_merchant' => config('variables.yoomee_username'), + 'order_merchant_password' => config('variables.yoomee_password'), + 'order_type' => 'MemberAccount.merchantPaymentWithoutFees', + 'order_payer' => $customer_phone_number, + 'order_method' => $payment_method, + 'order_app_id' => config('variables.yoomee_app_id'), + 'order_ext_id' => $transaction_id, + "order_base_amount" => $request->input('amount'), + "order_currency" => $request->input('currency'), + "order_description" => $request->input('reason'), + "order_wait_final_status" => $this->isSyncRequest + ] + ]); + + + if ($createResponse->getStatusCode() == 400) { + $createResponse = json_decode($createResponse->getBody()->getContents()); + + $transaction = PaymentTransaction::create([ + 'aggregator_id' => $request->input('aggregator_id'), + "currency" => $request->input('currency'), + 'aggregator_payment_ref' => $createResponse->transaction_number, + "transaction_id" => $transaction_id, + "amount" => $createResponse->transaction_amount, + 'payment_date' => $createResponse->transaction_date, + "payment_method" => $payment_method, + 'payment_token' => Str::random(32), + 'state' => strtoupper($createResponse->transaction_status), + "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" => $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'), + ]); + + if($transaction->state == PaymentTransactionState::PENDING){ + return $this->successResponse([ + 'verification_url' => route('yoomee.v2.webhook',['transaction_id' => $transaction_id]) + ]); + }else{ + return $this->successResponse([ + 'transaction_id' => $transaction_id, + 'token' => $transaction->payment_token + ]); + } + } + + + return $this->errorResponse(__('errors.unexpected_error')); + } + + public function capturePaymentResult(Request $request) + { + $this->validate($request, [ + 'transaction_id' => 'required|string|exists:payment_transactions,transaction_id' + ]); + + $transaction = PaymentTransaction::where('transaction_id',$request->input('transaction_id'))->first(); + return $this->getPaymentStatus($transaction); + } + + private function getPaymentStatus(PaymentTransaction $transaction) + { + try { + $response = $this->client->post('status/v1', [ + 'json' => [ + 'order_merchant' => config('variables.yoomee_username'), + 'order_merchant_password' => config('variables.yoomee_password'), + 'order_method' => $transaction->payment_method, + 'order_app_id' => config('variables.yoomee_app_id'), + 'order_ext_id' => $transaction->transaction_id, + "order_wait_final_status" => $this->isSyncRequest + ] + ]); + + $responseData = json_decode($response->getBody()->getContents()); + $responseCode = $response->getStatusCode(); + + if ($responseCode == 400) { + + $state = strtoupper($responseData->transaction_status); + if($state == 'SUCCESS'){ + $state = PaymentTransactionState::ACCEPTED; + }else{ + if(str_starts_with($state,'C')){ + $state = PaymentTransactionState::REFUSED; + } + } + + $transaction->update([ + 'state' => $state, + 'payment_date' => $responseData->transaction_date ?? null, + ]); + } + + } catch (Throwable $e) { + Log::info("Get Yoomee Payment Status Error"); + Log::info($e->getMessage()); + $transaction->update([ + 'state' => PaymentTransactionState::REFUSED + ]); + + } + + if ($transaction->state == PaymentTransactionState::ACCEPTED) { + return $this->successResponse([ + 'transaction_id' => $transaction->transaction_id, + 'token' => $transaction->payment_token + ]); + } else { + return $this->errorResponse("Payment failed"); + } + + } + +} diff --git a/config/variables.php b/config/variables.php index b258ddd..5b03b47 100644 --- a/config/variables.php +++ b/config/variables.php @@ -6,6 +6,10 @@ return [ 'swagger_docs_token' => env('SWAGGER_DOCS_TOKEN', true), 'yoomee_merchant_code' => env('YOOMEE_MERCHANT_CODE', ''), 'yoomee_api_url' => env('YOOMEE_API_URL', ''), + 'yoomee_api_v2_url' => env('YOOMEE_API_V2_URL', ''), + 'yoomee_app_id' => env('YOOMEE_APP_ID', ''), + 'yoomee_username' => env('YOOMEE_USERNAME', ''), + 'yoomee_password' => env('YOOMEE_PASSWORD', ''), 'cinetpay_api_key' => env('CINETPAY_API_KEY', ''), 'cinetpay_secret_key' => env('CINETPAY_SECRET_KEY', ''), 'cinetpay_site_id' => env('CINETPAY_SITE_ID', ''), diff --git a/routes/web.php b/routes/web.php index c532a81..5f8b7c7 100644 --- a/routes/web.php +++ b/routes/web.php @@ -16,7 +16,7 @@ /** * Webhooks */ -$router->addRoute(['GET','POST'],'/yoomee/webhook', ['as' => 'yoomee.webhook' , 'uses' => 'YoomeeController@capturePaymentResult']); +$router->addRoute(['GET','POST'],'/yoomee/v2/webhook', ['as' => 'yoomee.v2.webhook' , 'uses' => 'YoomeeV2Controller@capturePaymentResult']); $router->addRoute(['GET','POST'],'/cinetpay/webhook', ['as' => 'cinetpay.webhook' , 'uses' => 'CinetpayController@capturePaymentResult']); $router->group(['prefix' => '', 'middleware' => 'auth'], function () use ($router) { @@ -24,6 +24,7 @@ $router->group(['prefix' => '', 'middleware' => 'auth'], function () use ($route /** * Entry Endpoints */ + $router->post('methods','PaymentController@getMethods'); $router->post('pay','PaymentController@pay'); @@ -31,15 +32,20 @@ $router->group(['prefix' => '', 'middleware' => 'auth'], function () use ($route * Yoomee Endpoints */ $router->group(['prefix' => 'yoomee'], function () use ($router) { - $router->get('operators','YoomeeController@getOperators'); + $router->get('methods',['as' => 'yoomee.methods', 'uses' => 'YoomeeController@getMethods']); $router->addRoute(['GET','POST'],'pay', ['as' => 'yoomee.pay', 'uses' => 'YoomeeController@pay']); + + $router->group(['prefix' => 'v2'] , function () use ($router){ + $router->get('methods',['as' => 'yoomee.v2.methods', 'uses' => 'YoomeeV2Controller@getMethods']); + $router->addRoute(['GET','POST'],'pay', ['as' => 'yoomee.v2.pay', 'uses' => 'YoomeeV2Controller@pay']); + }); }); /** * CinetPay Endpoints */ $router->group(['prefix' => 'cinetpay'], function () use ($router) { - $router->get('operators','CinetpayController@getOperators'); + $router->get('methods',['as' => 'cinetpay.methods', 'uses' => 'CinetpayController@getMethods']); $router->addRoute(['GET','POST'],'pay',['as' => 'cinetpay.pay', 'uses' => 'CinetpayController@pay']); }); });