475 lines
20 KiB
PHP
475 lines
20 KiB
PHP
<?php
|
||
|
||
namespace App\Http\Controllers;
|
||
|
||
use App\Enums\PaymentTransactionStatus;
|
||
use App\Models\PaymentAggregator;
|
||
use App\Models\PaymentTransaction;
|
||
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 Throwable;
|
||
|
||
class CinetpayController 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 Client([
|
||
'base_uri' => config('variables.cinetpay_api_url')
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* @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" : {"hasWebview": true, "methods": { "MOBILE_MONEY": "Mobile Money", "CREDIT_CARD": "Carte de crédit" }},
|
||
* "error":null
|
||
* }
|
||
* )
|
||
* )
|
||
* )
|
||
*/
|
||
public function getMethods()
|
||
{
|
||
$providers = [
|
||
// 'ALL',
|
||
'MOBILE_MONEY',
|
||
'CREDIT_CARD',
|
||
];
|
||
$methods = [];
|
||
foreach ($providers as $provider) {
|
||
$key = 'providers.' . $provider;
|
||
$method['title'] = Lang::has($key) ? __($key) : $provider;
|
||
$method['value'] = $provider;
|
||
$methods[] = $method;
|
||
}
|
||
return $this->successResponse([
|
||
'hasWebview' => true,
|
||
'methods' => $methods,
|
||
]);
|
||
}
|
||
|
||
//
|
||
public function pay(Request $request)
|
||
{
|
||
$this->validate($request, [
|
||
'aggregator_id' => 'required|integer',
|
||
'amount' => 'required|numeric|min:5',
|
||
'currency' => 'required|string|size:3',
|
||
'payment_method' => 'nullable|string|in:ALL,MOBILE_MONEY,CREDIT_CARD,WALLET',
|
||
'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',
|
||
'customer_city' => 'required_if:payment_method,CREDIT_CARD|string',
|
||
'customer_country' => 'required|string|size:2',
|
||
'customer_state' => 'required_if:payment_method,CREDIT_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,CREDIT_CARD|string|size:5',
|
||
'reason' => 'required|string'
|
||
]);
|
||
|
||
$transaction_id = $this->getTransactionID();
|
||
$payment_method = $request->input('payment_method','ALL');
|
||
$amount = $request->input('amount');
|
||
$currency = $request->input('currency');
|
||
|
||
if ($currency != 'USD') {
|
||
// Convertir en multiple de 5
|
||
$amount = $this->roundUpToAny($amount);
|
||
}
|
||
|
||
|
||
// Init payment
|
||
$createResponse = $this->client->post('payment', [
|
||
'json' => [
|
||
"api_key" => config('variables.cinetpay_api_key'),
|
||
"site_id" => config('variables.cinetpay_site_id'),
|
||
"transaction_id" => $transaction_id,
|
||
"amount" => $amount,
|
||
"currency" => $request->input('currency'),
|
||
"description" => $request->input('reason'),
|
||
"notify_url" => route('cinetpay.webhook'),
|
||
"return_url" => route('cinetpay.webhook'),
|
||
"channels" => $payment_method,
|
||
'lang' => app()->getLocale(),
|
||
"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'),
|
||
],
|
||
'timeout' => $this->timeout
|
||
]);
|
||
|
||
$responseData = json_decode($createResponse->getBody()->getContents());
|
||
$responseCode = $createResponse->getStatusCode();
|
||
|
||
if ($responseCode == 200) {
|
||
|
||
PaymentTransaction::create([
|
||
'aggregator_id' => $request->input('aggregator_id'),
|
||
"currency" => $request->input('currency'),
|
||
"transaction_id" => $transaction_id,
|
||
"amount" => $amount,
|
||
"payment_method" => $payment_method,
|
||
"payment_token" => $responseData->data->payment_token,
|
||
"payment_url" => $responseData->data->payment_url,
|
||
'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->payment_url
|
||
]);
|
||
|
||
}else{
|
||
return $this->errorResponse($responseData->error ?? trans('errors.unexpected_error'),$responseCode);
|
||
}
|
||
}
|
||
|
||
|
||
public function payOut(Request $request)
|
||
{
|
||
$this->validate($request, [
|
||
// 'aggregator_id' => 'required|integer',
|
||
'amount' => 'required|numeric|min:5',
|
||
'currency' => 'required|string|size:3',
|
||
'customer_id' => 'nullable',
|
||
// 'payment_method' => 'required|string|in:WALLET',
|
||
'customer_email' => 'required|email',
|
||
'customer_name' => 'nullable|string',
|
||
'customer_surname' => 'required|string',
|
||
'customer_phone_number' => ['nullable','string',(new Phone())->country(['CI','SN','ML','CM','TG','BF','CD','GN','BJ'])],
|
||
'customer_address' => 'nullable|string',
|
||
'customer_city' => 'nullable|string',
|
||
'customer_country' => 'required|string|size:2|in:CI,SN,ML,CM,TG,BF,CD,GN,BJ',
|
||
'reason' => 'required|string'
|
||
]);
|
||
|
||
$aggregator = PaymentAggregator::where('name','like','%cinetpay%')->firstOrFail();
|
||
|
||
try{
|
||
|
||
$country_code = $request->input('customer_country');
|
||
$phoneNumber = str_replace(' ','',$request->input('customer_phone_number'));
|
||
|
||
$phone = new PhoneNumber($phoneNumber, $country_code);
|
||
$phoneNumber = str_replace(' ','',$phone->formatInternational());
|
||
$nationalPhone = str_replace(' ','',$phone->formatNational());
|
||
$phonePrefix = substr($phoneNumber, 1, strlen($phoneNumber) - strlen($nationalPhone) - 1);
|
||
|
||
$amount = $request->input('amount');
|
||
$payment_method = 'WALLET';
|
||
|
||
if($amount < 500){
|
||
return $this->errorResponse('Minimun amount is 500');
|
||
}
|
||
|
||
|
||
$client = new Client([
|
||
'base_uri' => config('variables.cinetpay_transfert_url')
|
||
]);
|
||
|
||
// Login
|
||
$loginResponse = $client->post('auth/login', [
|
||
'form_params' => [
|
||
"apikey" => config('variables.cinetpay_api_key'),
|
||
"password" => config('variables.cinetpay_transfert_password'),
|
||
],
|
||
'timeout' => $this->timeout,
|
||
'http_errors' => false
|
||
]);
|
||
|
||
$responseData = json_decode($loginResponse->getBody()->getContents());
|
||
$token = $responseData->data->token;
|
||
$responseCode = $loginResponse->getStatusCode();
|
||
if (!empty($token)) {
|
||
|
||
// Add Contact
|
||
$contactResponse = $client->post('transfer/contact', [
|
||
'query' => [
|
||
'token' => $token
|
||
],
|
||
'form_params' => [
|
||
"data" => json_encode(
|
||
[
|
||
[
|
||
'prefix' => $phonePrefix,
|
||
'phone' => $nationalPhone,
|
||
'name' => $request->input('customer_name'),
|
||
'surname' => $request->input('customer_surname'),
|
||
'email' => $request->input('customer_email')
|
||
]
|
||
]
|
||
),
|
||
],
|
||
'timeout' => $this->timeout,
|
||
'http_errors' => false
|
||
]);
|
||
|
||
$responseCode = $contactResponse->getStatusCode();
|
||
|
||
|
||
if ($responseCode == 200) {
|
||
|
||
$transactionId = $this->getTransactionID();
|
||
$transaction = PaymentTransaction::create([
|
||
'aggregator_id' => $aggregator->id,
|
||
"currency" => $request->input('currency'),
|
||
"transaction_id" => $transactionId,
|
||
"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" => $phoneNumber,
|
||
"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'),
|
||
]);
|
||
|
||
|
||
// Transfert Fund
|
||
$transfertResponse = $client->post('transfer/money/send/contact', [
|
||
'query' => [
|
||
'token' => $token
|
||
],
|
||
'form_params' => [
|
||
"data" => json_encode(
|
||
[
|
||
[
|
||
'client_transaction_id' => $transactionId,
|
||
'amount' => $amount,
|
||
'notify_url' => route('cinetpay.transfert.webhook'),
|
||
'prefix' => $phonePrefix,
|
||
'phone' => $nationalPhone,
|
||
]
|
||
]
|
||
),
|
||
],
|
||
'timeout' => $this->timeout,
|
||
'http_errors' => false
|
||
]);
|
||
|
||
$responseData = json_decode($transfertResponse->getBody()->getContents());
|
||
$responseCode = $transfertResponse->getStatusCode();
|
||
|
||
if ($responseCode == 200) {
|
||
|
||
$transaction->update([
|
||
'aggregator_payment_ref' => $responseData->data[0]?->transaction_id,
|
||
'status' => PaymentTransactionStatus::PENDING,
|
||
]);
|
||
|
||
return $this->successResponse([
|
||
'message' => 'Transfert is pending',
|
||
'transaction_id' => $transactionId,
|
||
'transaction_status' => $transaction->status
|
||
]);
|
||
}
|
||
}
|
||
}
|
||
|
||
$errorMessage = $responseData?->description ?? $responseData?->message;
|
||
|
||
}catch (Throwable $e){
|
||
Log::error("Error CinetPay transfert payment");
|
||
$errorMessage = $e->getMessage();
|
||
Log::error($errorMessage);
|
||
}
|
||
|
||
return $this->errorResponse($errorMessage ?? __('errors.unexpected_error'));
|
||
}
|
||
|
||
public function captureTransfertResult(Request $request)
|
||
{
|
||
$this->validate($request, [
|
||
'transaction_id' => 'nullable|string',
|
||
'client_transaction_id' => 'nullable|string|exists:payment_transactions,transaction_id'
|
||
]);
|
||
|
||
if($request->has('transaction_id') && $request->has('client_transaction_id')){
|
||
$transaction = PaymentTransaction::where('transaction_id',$request->input('client_transaction_id'))->firstOrFail();
|
||
try {
|
||
|
||
$client = new Client([
|
||
'base_uri' => config('variables.cinetpay_transfert_url')
|
||
]);
|
||
|
||
// Login
|
||
$loginResponse = $client->post('auth/login', [
|
||
'form_params' => [
|
||
"apikey" => config('variables.cinetpay_api_key'),
|
||
"password" => config('variables.cinetpay_transfert_password'),
|
||
],
|
||
'timeout' => $this->timeout
|
||
]);
|
||
|
||
$responseData = json_decode($loginResponse->getBody()->getContents());
|
||
$token = $responseData->data->token;
|
||
$responseCode = $loginResponse->getStatusCode();
|
||
if ($responseCode == 200 && !empty($token)) {
|
||
|
||
$response = $client->get('transfer/check/money', [
|
||
'query' => [
|
||
'token' => $token,
|
||
'transaction_id' => $request->input('transaction_id')
|
||
],
|
||
]);
|
||
|
||
$responseData = json_decode($response->getBody()->getContents());
|
||
$responseCode = $response->getStatusCode();
|
||
if ($responseCode == 200) {
|
||
|
||
$transaction->update([
|
||
'status' => $responseData->data->status,
|
||
'payment_method_exact' => $responseData->data->payment_method ?? null,
|
||
'payment_date' => $responseData->data->payment_date ?? null,
|
||
]);
|
||
|
||
}
|
||
}
|
||
|
||
} catch (Throwable $e) {
|
||
Log::info("Get Cinetpay Transfert Status Error");
|
||
$errorMessage = $e->getMessage();
|
||
Log::info($errorMessage);
|
||
$transaction->update([
|
||
'status' => PaymentTransactionStatus::REFUSED
|
||
]);
|
||
}
|
||
|
||
return $this->errorResponse($errorMessage ?? __('errors.unexpected_error'));
|
||
}else{
|
||
return response("OK");
|
||
}
|
||
}
|
||
|
||
|
||
public function capturePaymentResult(Request $request)
|
||
{
|
||
$this->validate($request, [
|
||
'cpm_site_id' => 'nullable|string',
|
||
'cpm_trans_id' => 'nullable|string|exists:payment_transactions,transaction_id'
|
||
]);
|
||
|
||
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') .
|
||
$request->input('signature') . $request->input('payment_method') . $request->input('cel_phone_num') . $request->input('cpm_phone_prefixe') .
|
||
$request->input('cpm_language') . $request->input('cpm_version') . $request->input('cpm_payment_config') . $request->input('cpm_page_action') . $request->input('cpm_custom') . $request->input('cpm_designation') . $request->input('cpm_error_message');
|
||
|
||
$generated_token = hash_hmac('SHA256', $data, config('variables.cinetpay_secret_key'));
|
||
$received_token = $request->header('x-token');
|
||
|
||
if(hash_equals($received_token, $generated_token)) {
|
||
$transaction = PaymentTransaction::where('transaction_id',$request->input('cpm_trans_id'))->firstOrFail();
|
||
return $this->getPaymentStatus($transaction);
|
||
}else{
|
||
return $this->errorResponse("Invalid token");
|
||
}
|
||
|
||
}else if($request->has('transaction_id')){
|
||
$transaction = PaymentTransaction::where('transaction_id',$request->input('transaction_id'))->firstOrFail();
|
||
return $this->getPaymentStatus($transaction);
|
||
}else{
|
||
return response("OK");
|
||
}
|
||
}
|
||
|
||
private function getPaymentStatus(PaymentTransaction $transaction)
|
||
{
|
||
try {
|
||
|
||
$response = $this->client->post('payment/check', [
|
||
'json' => [
|
||
"apikey" => config('variables.cinetpay_api_key'),
|
||
"site_id" => config('variables.cinetpay_site_id'),
|
||
'transaction_id' => $transaction->transaction_id,
|
||
]
|
||
]);
|
||
|
||
$responseData = json_decode($response->getBody()->getContents());
|
||
$responseCode = $response->getStatusCode();
|
||
|
||
if ($responseCode == 200) {
|
||
|
||
$transaction->update([
|
||
'status' => $responseData->data->status,
|
||
'payment_method_exact' => $responseData->data->payment_method ?? null,
|
||
'aggregator_payment_ref' => $responseData->data->operator_id ?? null,
|
||
'payment_date' => $responseData->data->payment_date ?? 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
|
||
]);
|
||
}
|
||
|
||
}
|
||
}
|